本文是閱讀 Head First 設計模式——裝飾者模式的總結。 這本書的教學模式很不錯,我的很喜歡,由實際的案例由淺入深,按部就班的讓你明白良好的設計是多麼的優雅迷人(回頭看看本身的代碼,WTF!)。 可是讀第二遍的時候,竟然想不起來這章節說了什麼,到底怎麼解決這個問題的。也就是說,看的時候爽快,看完了並無應用到具體的Coding中。java
這一章節的案例是:設計星巴茲咖啡系統設計模式
相關的背景:
星巴茲咖啡如今有四種咖啡:黑咖啡(HouseBlend)、深度烘焙咖啡(DarkRoast)、脫咖啡因咖啡(Decaf)、濃咖啡(Espresso)。
用戶在購買咖啡時,能夠要求在其中加入各類調料,例如:蒸奶(Steamed Milk)、豆漿(Soy)、摩卡(Mocha)或覆蓋奶泡(Whip)。會根據所加入的調料收取不一樣的費用,因此訂單系統必須考慮到這些調料的價格。ide
若是按照這種模式,在某種咖啡中加入調料,那麼就是一個新的子類,繼承 Beverage
,實現本身的 cost()
方法,算出咖啡以及調料的價格。以此類推,這是一個類爆炸的系統,有多少種花樣就要爲此設計多少類。測試
很明顯,星巴茲爲本身製做了一個維護噩夢。若是牛奶漲價了,怎麼辦?新增一種焦糖風味調料時,怎麼辦?優化
很明顯,這種設計是有致命的缺陷的。this
利用實例變量和繼承,能夠追蹤調料,不必去設計這麼多類。spa
這樣設計有哪些缺陷呢?設計
cost()
方法固然這種設計,違反了基本的開閉原則,類應該對擴展開放,對修改關閉。3d
從上面的方案來看,咱們利用繼承沒法徹底解決問題,如今遇到的問題有:類數量爆炸、設計死板,以及基類加入的新功能並不適用全部的子類。code
因此,在這裏要採用不同的作法:以飲料爲主體,而後再運行時以調料來「裝飾」(decorate)飲料。好比,若是顧客想要摩卡和奶泡深焙咖啡,那麼,要作的是:
cost()
方法,並依賴委託將調料的價錢加上去上面是pdf文本的截圖,這個過程若是不畫出來,就漏掉了很重要的按部就班的過程。
裝飾者模式:動態的將責任附加到對象上。如果要擴展功能,裝飾者提供了比繼承更有彈性的替代方案。
上圖是裝飾者模式的結構類圖。
廢話很少說,看看咱們的訂單系統該如何寫。
原始的類圖中,超類Beverage基本不用改動。
public abstract class Beverage {
String description = "";
public String getDescription() {
return description;
}
public abstract double cost();
}
複製代碼
下面實現調料類的抽象類,也就是裝飾者類:
// 爲了讓CondimentDecorator 可以取代 Beverage,全部才繼承Beverage
public abstract class CondimentDecorator extends Beverage {
public abstract String getDescription();
}
複製代碼
下面是飲料實體類,按照上面的包裝圖例,咱們就實現DarkRoast
就行了。
public class DarkRoast extends Beverage {
public DarkRoast() {
description = "DarkRoast";
}
@Override
public double cost() {
return 1.99;
}
}
複製代碼
下面是Mocha 和 Whip 的調料代碼
public class Mocha extends CondimentDecorator {
Beverage beverage;
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ", Mocha";
}
public double cost() {
return 0.20 + beverage.cost();
}
}
複製代碼
public class Whip extends CondimentDecorator {
Beverage beverage;
public Whip(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ", Whip";
}
public double cost() {
return 0.10 + beverage.cost();
}
}
複製代碼
好了,依照裝飾者模式,咱們完成了基本的代碼實現。下面是測試代碼:
public class StarbuzzCoffee {
public static void main(String[] args) {
// 一杯DarkRoast,不須要調料
Beverage beverage = new DarkRoast();
System.out.println(beverage.getDescription() + " $" + beverage.cost());
// 一杯DarkRoast,加雙份Mocha和奶泡
Beverage beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2);
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
}
}
複製代碼
運行結果以下:
DarkRoast $1.99
DarkRoast, Mocha, Mocha, Whip $2.49
Process finished with exit code 0
複製代碼
真實世界的裝飾者:Java I/O
文章原本是打算簡單的總結下該章節的內容,方便之後資料的查找,回憶下知道大概講的是什麼東西,寫下來發現,若是不可以將這個按部就班的過程寫下來,那麼就失去這本書基本的宗旨。 上面的內容基本都是章節的文本,主要是擔憂本身理解誤導了讀者。
若是對文章的內容感興趣,不妨去讀一下《Head First設計模式》。