Java設計模式の裝飾者模式
目錄
一、問題引入
二、設計原則
三、用裝飾者模式解決問題
四、裝飾者模式的特點
五、裝飾者模式的定義
六、裝飾者模式的實現
七、java.io包內的裝飾者模式
一、問題引入
咖啡店的類設計:
一個飲料基類,各種飲料類繼承這個基類,並且計算各自的價錢。
飲料中需要加入各種調料,考慮在基類中加入一些布爾值變量代表是否加入各種調料,基類的cost()中的計算各種調料的價錢,子類覆蓋cost(),並且在其中調用超類的cost(),加上特定飲料的價錢,計算出子類特定飲料的價錢。
缺點:類數量爆炸、基類加入的新功能並不適用於所有的子類、調料價錢的改變、新調料的出現都會要求改變現有代碼;有的子類並不適合某些調料等情況……
二、設計原則
類應該對擴展開放,對修改關閉。
我們的目標是允許類容易擴展,在不修改現有代碼的情況下,就可搭配新的行為。
如能實現這樣的目標,有什麽好處呢?這樣的設計具有彈性可以應對改變,可以接受新的功能來應對改變的需求。
要讓OO設計同時具備開放性和關閉性,不是一件容易的事,通常來說,沒有必要把設計的每個部分都這麽設計。
遵循開放-關閉原則,通常會引入新的抽象層次,增加代碼的復雜度。
我們需要把註意力集中在設計中最有可能改變的地方,然後應用開放-關閉原則。
三、用裝飾者模式解決問題
解決咖啡店飲料問題的方法:
以飲料為主體,然後在運行時以調料來“裝飾”飲料。
比如,顧客想要摩卡(Mocha)和奶泡(Whip)深焙咖啡(DarkRoast):
DarkRoast繼承自Beverage,有一個cost()方法。
第一步,以DarkRoast對象開始;
第二步,顧客想要摩卡,所以建立一個Mocha裝飾者對象,並用它將DarkRoast對象包裝(wrap)起來;
第三步,顧客想要奶泡,所以建立一個Whip裝飾者對象,並用它將Mocha對象包起來;(Mocha和Whip也繼承自Beverage,有一個cost()方法);
最後,為顧客算錢,通過調用最外圈裝飾者(Whip)的cost()就可以。Whip()的cost()會先委托它裝飾的對象(Mocha)計算出價錢,然後在加上奶泡的價錢。Mocha的cost()也是類似。
四、裝飾者模式的特點
裝飾者和被裝飾對象有相同的超類型。
可以用一個或多個裝飾者包裝一個對象。
因為裝飾者和被裝飾者具有相同的類型,所以任何需要原始對象的場合,可以用裝飾過的對象代替。
裝飾者可以在所委托被裝飾者的行為之前與/或之後,加上自己的行為,以達到特定的目的。
對象可以在任何時候被裝飾,所以可以在運行時動態地、不限量地用你喜歡的裝飾者來裝飾對象。
五、裝飾者模式的定義
裝飾者模式動態地將責任附加到對象上。若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案。
六、裝飾者模式的實現
實現類圖如下:
裝飾者和被裝飾者具有共同的超類,利用繼承達到“類型匹配”,而不是利用繼承獲得“行為”;將裝飾者和被裝飾者組合時,加入新的行為。
詳細源碼:
package com.blankjor.decorator; /** * @desc 抽象飲料類(抽象組件) * @author Blankjor * @date 2017年5月14日 上午11:14:06 */ public abstract class Beverage { String description = "Unkown Beverage"; public String getDescription() { return description; } /** * 抽象價格計算方法 * * @return */ public abstract double cost(); } package com.blankjor.decorator; /** * @desc 濃縮飲料 (具體組件) * @author Blankjor * @date 2017年5月14日 上午11:20:11 */ public class Espresso extends Beverage { public Espresso() { description = "Espresso"; } @Override public double cost() { return 1.99; } } package com.blankjor.decorator; /** * @desc House Blend 飲料(具體組件) * @author Blankjor * @date 2017年5月14日 上午11:20:11 */ public class HouseBlend extends Beverage { public HouseBlend() { description = "House Blend"; } @Override public double cost() { return .20; } } package com.blankjor.decorator; /** * @desc 抽象裝飾者類 * @author Blankjor * @date 2017年5月14日 上午11:17:12 */ public abstract class CondimentDecorator extends Beverage { /** * 為了後面的調料都能夠獲取到自己調料的描述 */ public abstract String getDescription(); } package com.blankjor.decorator; /** * @desc Mocha調料(具體裝飾者) * @author Blankjor * @date 2017年5月14日 上午11:23:36 */ public class Mocha extends CondimentDecorator { Beverage beverage; public Mocha(Beverage beverage) { this.beverage = beverage; } @Override public String getDescription() { return beverage.getDescription() + ",Mocha"; } @Override public double cost() { return .20 + beverage.cost(); } } package com.blankjor.decorator; /** * @desc Soy調料(具體裝飾者) * @author Blankjor * @date 2017年5月14日 上午11:20:11 */ public class Soy extends CondimentDecorator { Beverage beverage; public Soy(Beverage beverage) { this.beverage = beverage; } @Override public String getDescription() { return beverage.getDescription() + ",Soy"; } @Override public double cost() { return .60 + beverage.cost(); } } package com.blankjor.decorator; /** * @desc Whip調料(具體裝飾者) * @author Blankjor * @date 2017年5月14日 上午11:20:11 */ public class Whip extends CondimentDecorator { Beverage beverage; public Whip(Beverage beverage) { this.beverage = beverage; } @Override public String getDescription() { return beverage.getDescription() + ",Whip"; } @Override public double cost() { return .40 + beverage.cost(); } } package com.blankjor.decorator; /** * @desc 測試裝飾者模式 * @author Blankjor * @date 2017年5月14日 上午11:29:50 */ public class MainTest { public static void main(String[] args) { // 創建一種調料 Beverage beverage = new Espresso(); // 描述和價格 System.out.println(beverage.getDescription() + " $" + beverage.cost()); Beverage beverage1 = new HouseBlend(); beverage1 = new Mocha(beverage1); beverage1 = new Whip(beverage1); beverage1 = new Soy(beverage1); System.out.println(beverage1.getDescription() + " $" + beverage1.cost()); Beverage beverage2 = new Espresso(); beverage2 = new Mocha(beverage2); beverage2 = new Whip(beverage2); beverage2 = new Soy(beverage2); beverage2 = new Mocha(beverage2); System.out.println(beverage2.getDescription() + " $" + beverage2.cost()); } }
運行結果:
裝飾者和被裝飾者具有共同的超類,利用繼承達到“類型匹配”,而不是利用繼承獲得“行為”;將裝飾者和被裝飾者組合時,加入新的行為。
解決本文中飲料的具體問題時,圖中Component即為Beverage(可以是抽象類或者接口),而ConcreteComponent為各種飲料,Decorator(抽象裝飾者)為調料的抽象類或接口,ConcreteDecoratorX則為各種具體的調料。
因為使用對象組合,可以把飲料和調料更有彈性地加以混合與匹配。
代碼外部細節:
代碼中實現的時候,通過構造函數將被裝飾者傳入裝飾者中即可,如最後的調用形式如下:
Beverage beverage = new DarkRoast();
beverage = new Mocha(beverage);
beverage = new Whip(beverage);
即完成了兩層包裝,此時再調用beverage的cost()函數即可得到總價。
七、java.io包內的裝飾者模式
裝飾者模式的缺點:在設計中加入大量的小類,如果過度使用,會讓程序變得復雜。
參考:http://www.cnblogs.com/mengdd/archive/2013/01/03/2843439.html
Java設計模式の裝飾者模式