一、引言
23種設計模式大概分為三大類:
5種(建立型模式):工廠方法模式、抽象工廠模式、單例模式、原型模式、建造者模式。
7種(結構型模式):擴充卡模式、橋接模式、代理模式、外觀模式、裝飾者模式、組合模式、享元模式。
11種(行為型模式):政策模式、模闆方法模式、觀察者模式、疊代器模式、責任鍊模式、指令模式、備忘錄模式、狀态模式、通路者模式、中介者模式、解釋器模式。
行為型又可以通過類與類之間的關系進行劃分 :
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIwczX0xiRGZkRGZ0Xy9GbvNGL2EzXlpXazxSPVpnTz0EVNFTV61EMFRkW0A3MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZwpmL2UTN3MzNwIjM2EzNwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
裝飾者模式基本介紹:
- 裝飾者模式(Decorator):動态地将新功能附加到對象上,在對象功能擴充方面,它比繼承更有彈性,裝飾者模式也展現了開閉原則(ocp)
二、裝飾者模式
1.裝飾者模式原理類圖
-
裝飾者模式就像打包一個快遞
主體:比如:陶瓷、衣服 (Component) //被裝飾者
包裝:比如:報紙填充、塑膠泡沫、紙闆、木闆(Decorator) //裝飾者
- Component: 主體
- ConcreteComponent 和 Decorator
- ConcreteComponent:具體的主體
- Decorator:裝飾者,比如各調料,裝飾者裡聚合了一個Component,也就是說,ConcreteComponent是可以放到裝飾者裡面的(裝飾者裡面可以包含被裝飾者)
- 在如圖的 Component 與 ConcreteComponent 之間,如果 ConcreteComponent 類很多,還可以設計一個緩沖層,将共有的部分提取出來,抽象成一個類。
三、具體需求
1.星巴克咖啡
- 星巴克咖啡館
- 咖啡種類/單品咖啡:Espresso(意大利濃咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(無因咖啡)
- 調料:Milk、Soy(豆漿)、Chocolate
要求在擴充新的咖啡種類時,具有良好的擴充性、改動友善、維護友善。
要求計算不同種類咖啡的費用: 客戶可以點單品咖啡,也可以單品咖啡+調料組合。
2. 傳統方案
思路一分析 - 類圖
類圖分析:
- Drink 是一個抽象類,表示飲料
- description 就是對咖啡的描述,比如咖啡的名字
- cost()方法就是計算費用,Drink 類中做成一個抽象方法
- Decaf 就是單品咖啡,繼承 Drink,并實作了 cost()
- Espress && Milk 就是單品咖啡+調料, 這個組合很多
方案一問題分析:這樣設計會有很多類,當需要增加一個單品咖啡,或者一個新的調料時,類的數量就會倍增,就會出現類爆炸
思路二分析
前面思路一因為咖啡單品+調料組合會造成類的倍增,是以可以做改進,将調料内置到 Drink 類,這樣就不會造成類數量過多,進而提高項目的維護性
說明:milk,soy,chocolate 可以設計為 Boolean,表示是否要添加相應的調料
方案二問題分析:
- 方案 2 可以控制類的數量,不至于造成很多的類
- 在增加或者删除調料種類時,代碼的維護量很大
- 考慮到使用者可以添加多份調料,可以将 hasMilk 傳回一個對應 int
- 改進:考慮使用 裝飾者 模式
3. 裝飾者模式方案
使用裝飾者模式改進傳統方式,讓程式具有更好的擴充性
思路分析 - 類圖
裝飾者模式下的訂單:2份巧克力 + 一份牛奶的LongBlack
說明:
- Milk包含了LongBlack
- 一份Chocolate包含了(Milk+LongBlack)
- 一份Chocolate包含了(Chocolate+Milk+LongBlack)
- 這樣,不管是什麼形式的單品咖啡+調料組合,通過遞歸方式都可以友善地組合和維護
具體實作
// 接口 Drink.java
public abstract class Drink {
public String des;//描述
private float price = 0.0f;
// get / set方法略
//計算費用的抽象方法:由子類實作
public abstract float cost();
}
// Coffee.java
public class Coffee extends Drink{
@Override
public float cost() {
return super.getPrice();
}
}
// Espresso.java/LongBlack.java/ShortBlack.java
public class Espresso extends Coffee{
public Espresso() {
setDes("意大利咖啡");
setPrice(6.0f);
}
}
// Decorator.java 繼承Drink又組合Drink
public class Decorator extends Drink{
private Drink drink;
public Decorator(Drink drink) {
this.drink = drink;
}
@Override
public float cost() {
//自己(調料)的價格+咖啡的價格
return super.getPrice() + drink.getPrice();
}
@Override
public String getDes() {
//父類的資訊+被裝飾者的資訊
return super.des+" "+super.getPrice()+" && "+drink.getDes();
}
}
// Chocolate.java/Milk.java/Soy.java
//具體的Decorator,這裡就是調味品
public class Chocolate extends Decorator {
public Chocolate(Drink drink) {//必須有構造器
super(drink);
setDes("巧克力");
setPrice(3.5f);
}
}
// CoffeeBar.java 使用
public class CoffeeBar {
public static void main(String[] args) {
//用裝飾者模式下單:2份Chocolate+1份milk+LongBlack
//1.先點一份LongBlack
Drink coffee = new LongBlack();
System.out.println("單品:"+coffee.getDes()+" 價格:"+coffee.cost());
//2.加入一份牛奶
coffee = new Milk(coffee);
System.out.println("加入一份牛奶後:"+coffee.getDes()+" 價格:"+coffee.cost());
//3.加入一份巧克力
coffee = new Chocolate(coffee);
System.out.println("加入一份牛奶+巧克力後:"+coffee.getDes()+" 價格:"+coffee.cost());
//4.再加入一份巧克力
coffee = new Chocolate(coffee);
System.out.println("加入一份牛奶+2份巧克力後:"+coffee.getDes()+" 價格:"+coffee.cost());
}
}