《Head First 設計模式》 學習筆記,碼雲同步更新中
如有錯誤或不足之處,請一定指出,謝謝~
往期回顧
裝飾者模式
- 定義:
- 動態地將責任附加到對象身上。若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案。
- 特點:
- 裝飾者和被裝飾着有共同的超類,裝飾過的對象可以替代原始對象使用
- 可以用一個或多個裝飾者包裝一個對象
- 裝飾者可以在所委託被裝飾者的行爲之前、之後,加上自己的行爲,來達到特定目的
- 注意:
- 這裏用到繼承的目的不是“繼承行爲”,而是“類型匹配”。對象的行爲來自於對象的組合。
這並不違反之前提到的設計原則。
- 這裏用到繼承的目的不是“繼承行爲”,而是“類型匹配”。對象的行爲來自於對象的組合。
- 優點:
- 可以很靈活地擴展對象功能,擴展時符合“開閉原則”
- 缺點:
- 會產生很多對象,增加系統複雜度,加大學習理解成本
- 使用時更容易出錯,錯誤排查也更加困難(但結合工廠模式和生成器模式後會得到很大改善)
- 案例
- 咖啡價格計算程序的最初實現:有一個咖啡的超類,所有品種的咖啡都會繼承他,並定義自己的描述和價格。
但帶來的問題是:不僅咖啡種類很多,當加入不同的配料(奶,焦糖,奶泡,摩卡)時,又會有不一樣的價格。
如果這樣實現,會產生茫茫多的咖啡子類。 - 裝飾者模式改造:
- 一杯加香草、榛子的美式咖啡的計價過程:
- new一個美式咖啡對象
- 用香草對象裝飾它
- 用榛子對象裝飾它
- 調用cost()方法,並依賴委託將配料的價格加上去
- 一杯加香草、榛子的美式咖啡的計價過程:
- 咖啡價格計算程序的最初實現:有一個咖啡的超類,所有品種的咖啡都會繼承他,並定義自己的描述和價格。
- 代碼
/**
* 飲料抽象超類
**/
public abstract class Beverage {
String description = "未知飲料";
public String getDescription() {
return description;
}
// 金額應該用BigDecimal
public abstract double cost();
}
/**
* 配料裝飾者超類
* 繼承飲料類
**/
public abstract class CondimentDecorator extends Beverage {
/**
* 這裏重寫的目的是約束配料類重寫獲取描述方法,
* 最終拿到完整的描述鏈
* 例如:“美式,香草,榛子”
*/
@Override
public abstract String getDescription();
}
/**
* 美式咖啡類
*/
public class Americano extends Beverage {
public Americano() {
description = "美式"; // 構造方法,修改繼承自超類的description
}
@Override
public double cost() {
return 21; // 返回美式本身的價格
}
}
/**
* 香草配料
**/
public class Vanilla extends CondimentDecorator {
Beverage beverage; // 用來記錄被裝飾者
public Vanilla(Beverage beverage) { // 構造函數,被裝飾者作爲參數
this.beverage = beverage;
}
@Override
public String getDescription() { // 獲取完整的描述
return beverage.getDescription() + ", 香草";
}
@Override
public double cost() { // 用被裝飾者的價格加上香草自己的價格
return 3 + beverage.cost();
}
}
/**
* 榛子配料
**/
public class Hazelnut extends CondimentDecorator {
Beverage beverage;
public Hazelnut(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ", 榛子";
}
@Override
public double cost() {
return 5 + beverage.cost();
}
}
/**
* 測試
*/
public class Test {
public static void main(String[] args) {
Beverage beverage = new Americano();
System.out.println(beverage.getDescription() + " ¥" + beverage.cost());
Beverage beverage2 = new Americano();
// 加香草
beverage2 = new Vanilla(beverage2);
// 加榛子
beverage2 = new Hazelnut(beverage2);
System.out.println(beverage2.getDescription() + " ¥" + beverage2.cost());
}
}
結果
美式 ¥21.0
美式, 香草, 榛子 ¥29.0
- Java中的裝飾者模式
- java.io 包中有茫茫多的類,但仔細觀察就會發現,其中很多類都是裝飾者。
這也體現了裝飾者模式的缺點:對於不明所以的人來說,大料API看起來會很困擾。 - 感性去的話可以自己編寫一個裝飾者來裝飾io,比如把輸入流中的所有小寫字母轉爲大寫。
- java.io 包中有茫茫多的類,但仔細觀察就會發現,其中很多類都是裝飾者。