文章目錄
設計模式-裝飾者模式
1. 案例引出裝飾者模式
星巴克咖啡訂單項目
- 咖啡種類:Espresso(意大利濃咖啡)、LongBlack(美式咖啡)、Decaf(無因咖啡)
- 調料:Milk、Chocolate。
- 要求在擴展新的咖啡種類時,
具有良好的擴展性、改動方便、維護方便
使用 OO 的來計算不同種類咖啡的費用: 客戶可以點單品咖啡,也可以單品咖啡+調料組合。
- 傳統實現方式如下:
傳統實現方式分析:
- Drink 是一個抽象類,表示抽象飲料。
- description 就是對咖啡的描述, 比如咖啡的名字、受歡迎程度等
- cost() 方法就是計算費用,Drink 類中做成一個抽象方法.
- Decaf 就是單品咖啡, 繼承 Drink, 並實重寫 cost方法。
- Espress && Milk 就是單品咖啡+調料, 這個組合很多
- 問題: 這樣設計會有很多類,當我們增加一個單品咖啡 , 或者一個新的調料 , 類的數量就會倍,就會出現
類爆炸
。
改進傳統方案:
- 前面分析到傳統方案 因爲咖啡單品+調料組合會造成類的倍增,因此可以做改進,將調料內置到 Drink 類,這樣就不會造成類數量過多。從而提高項目的維護性:
- 說明: milk,chocolate 可以設計爲 Boolean,表示是否要添加相應的調料。
改進後解決星巴克咖啡訂單問題的方案分析
- 改進後可以控制類的數量,不至於造成很多的類
在增加或者刪除調料種類時,代碼的維護量很大
- 考慮到用戶可以添加多份 調料時,可以將 hasMilk 返回一個對應 int
- 下面我們看一下設計模式中的裝飾模式,見識一下它的力量…
2. 裝飾者模式
2.1 裝飾者模式定義
- 裝飾者模式:
動態的將新功能附加到對象上。在對象功能擴展方面,它比繼承更有彈性
, 裝飾者模式也體現了開閉原則(ocp)。 - 這裏提到的動態的將新功能附加到對象和 ocp 原則,在後面的應用實例上會以代碼的形式體現。
2.2 裝飾者模式原理
裝飾者模式就像打包一個快遞
主體:比如:陶瓷、衣服,是被裝飾者 Component
包裝:比如:報紙填充、塑料泡沫、紙板、木板,是裝飾着Decorator
- Component 主體:比如類似前面的 Drink
- ConcreteComponent 和 Decorator
- ConcreteComponent:具體的主體,比如前面的各個單品咖啡
- Decorator: 裝飾者,比如各調料.
- 在如圖的 Component 與 ConcreteComponent 之間,如果 ConcreteComponent 類很多,還可以設計一個緩衝層,將共有的部分提取出來,抽象層一個類。
2.3 裝飾者模式解決星巴克咖啡訂單問題
2.4 代碼實現
結合着上圖來看:
抽象類Drink以及其子類
// 抽象類Drink
public abstract class Drink {
// 描述
public String des;
// 價格
private float price = 0.0f;
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
//計算費用的抽象方法
//子類來實現
public abstract float cost();
}
// 緩衝層Coffee
public class Coffee extends Drink {
@Override
public float cost() {
return super.getPrice();
}
}
// 具體的單品咖啡ShortBlack
public class ShortBlack extends Coffee{
public ShortBlack() {
setDes(" shortblack ");
setPrice(4.0f);
}
}
// 具體的單品咖啡LongBlack
public class LongBlack extends Coffee {
public LongBlack() {
setDes(" longblack ");
setPrice(5.0f);
}
}
裝飾者Decorator着及其子類☆
關鍵體現在Decorator這個類中:
Decorator繼承Drink類
Decorator聚合Drink類
Decorator組合Drink類
// 裝飾者
public class Decorator extends Drink {
// 聚合
private Drink obj;
// 組合
public Decorator(Drink obj) {
this.obj = obj;
}
@Override
public float cost() {
// getPrice 自己價格
return super.getPrice() + obj.cost();
}
@Override
public String getDes() {
// obj.getDes() 輸出被裝飾者的信息
return des + " " + getPrice() + " && " + obj.getDes();
}
}
//具體的Decorator, 這裏就是調味品Chocolate
public class Chocolate extends Decorator {
public Chocolate(Drink obj) {
super(obj);
setDes(" 巧克力 ");
setPrice(3.0f); // 調味品 的價格
}
}
//具體的Decorator, 這裏就是調味品Milk
public class Milk extends Decorator {
public Milk(Drink obj) {
super(obj);
// TODO Auto-generated constructor stub
setDes(" 牛奶 ");
setPrice(2.0f);
}
}
測試星巴克咖啡下單
裝飾者模式下的訂單:2份巧克力+ 1份牛奶的LongBlack
public class CoffeeBar {
public static void main(String[] args) {
// 裝飾者模式下的訂單:2份巧克力+一份牛奶的LongBlack
// 1. 單點一份 LongBlack
Drink order = new LongBlack();
System.out.println("費用1=" + order.cost());
System.out.println("描述=" + order.getDes());
// 2. 訂單 order 加入一份牛奶
order = new Milk(order);
System.out.println("order 加入一份牛奶 費用 =" + order.cost());
System.out.println("order 加入一份牛奶 描述 = " + order.getDes());
// 3. 訂單 order 加入一份巧克力
order = new Chocolate(order);
System.out.println("order 加入一份牛奶 加入一份巧克力 費用 =" + order.cost());
System.out.println("order 加入一份牛奶 加入一份巧克力 描述 = " + order.getDes());
}
}
3. 裝飾者模式在 JDK IO源碼體現分析
Java 的 IO 結構,FilterInputStream 就是一個裝飾者,源碼如下,通過源碼我們發現FilterInputStream與我們之前的Decorator有以下相同特徵:
FilterInputStream裝飾者繼承InputStream
FilterInputStream裝飾者聚合InputStream
FilterInputStream裝飾者組合InputStream
public class FilterInputStream extends InputStream {
/**
* 要過濾的輸入流。這裏使用聚合
*/
protected volatile InputStream in;
/**
* 通過將參數賦值給字段this來創建FilterInputStream。以便記住以後使用。
*
*/
protected FilterInputStream(InputStream in) {
this.in = in;
}
// ...
}
下面是JDK IO源碼InputStram中的源碼的UML類圖,你只要瞭解FilterInputStream類與抽象類InputStream之間的關係(繼承,組合,聚合),然後就很容易理解了。