08 裝飾者模式(Decorator Pattern)
阿新 • • 發佈:2018-11-05
描述性文字
還記得工廠方法模式中示例程式碼中的奶茶店嗎?在那一節中講解的是通過工廠方法模式來做奶茶,做奶茶的工作都已經交給小弟了, 小豬的工作量少了很多。於是,寫出所有飲品的價格:
奶茶:
- 原味奶茶:5塊
- 珍珠奶茶:7塊
- 椰果奶茶:7塊
- 珍珠椰果奶茶:9塊
檸檬茶:
- 原味檸檬茶:3塊
- 金桔檸檬茶:5塊.
然後顧客要什麼點什麼,按著選單收費就好了,然而使用者的 需求都是多變的,他們覺得配料那裡可以加點紅豆,然後你 的選單需要新增:
- 紅豆奶茶:7塊
- 紅豆珍珠奶茶:9塊
- 紅豆椰果奶茶:9塊
- 紅豆珍珠椰果奶茶:11塊
每個組合都寫一個,這他麼得寫多少個,而且使用者總是天馬行空的,哪天希望配料加點 ,也是有可能的, 每多一種配料,就得增加一堆飲品。我們必須想一個更優的套路,這個時候可以考慮引入裝飾者模式, 簡單來說就是:一層套一層,比如說要椰果珍珠奶茶:奶茶 –> 套一層珍珠 –> 珍珠(奶茶) –> 套一層椰果 –> 椰果(珍珠(奶茶))逼逼那麼多,程式碼演示下吧!
程式碼如下
package structPattrn.decoratorPattern; /** * 裝飾者模式測試例程 * * @Package structPattrn.decoratorPattern * @Title: DecoratorPatternDemo.java * @Company: $ * @author BurgessLee * @date 2018年10月16日-下午4:38:01 * @Description: $ */ public class DecoratorPatternDemo { public static void main(String[] args) { Tea mileTea = new MilkTea(); System.out.println("您點的是:" + mileTea.getName() +" 價格為:"+ mileTea.price()); Tea lemonTea = new LemonTea(); System.out.println("您點的是:" + lemonTea.getName() +" 價格為:"+ lemonTea.price()); Tea tea3 = new MilkTea(); System.out.println("您點的是:" + tea3.getName() +" 價格為:"+ tea3.price()); tea3 = new ZhenZhu(tea3); System.out.println("您點的是:" + tea3.getName() +" 價格為:"+ tea3.price()); tea3 = new YeGuo(tea3); System.out.println("您點的是:" + tea3.getName() +" 價格為:"+ tea3.price()); tea3 = new HongDou(tea3); System.out.println("您點的是:" + tea3.getName() +" 價格為:"+ tea3.price()); tea3 = new JinJu(tea3); System.out.println("您點的是:" + tea3.getName() +" 價格為:"+ tea3.price()); } } //抽象茶的父類 abstract class Tea{ private String name = "茶"; public String getName() { return name; } public void setName(String name) { this.name = name; } public abstract int price(); } //定義配料的父類 abstract class Decorator extends Tea{ public abstract String getName(); } class MilkTea extends Tea{ public MilkTea() { setName("奶茶"); } @Override public int price() { return 5; } } class LemonTea extends Tea{ public LemonTea() { setName("檸檬茶"); } @Override public int price() { return 3; } } class ZhenZhu extends Decorator{ private Tea tea; public ZhenZhu(Tea tea) { super(); this.tea = tea; } @Override public String getName() { return "珍珠" + this.tea.getName(); } @Override public int price() { return 2+ this.tea.price(); } } class YeGuo extends Decorator{ private Tea tea; public YeGuo(Tea tea) { super(); this.tea = tea; } @Override public String getName() { return "椰果" + this.tea.getName(); } @Override public int price() { return 2+ this.tea.price(); } } class HongDou extends Decorator{ private Tea tea; public HongDou(Tea tea) { super(); this.tea = tea; } @Override public String getName() { return "紅豆" + this.tea.getName(); } @Override public int price() { return 2+ this.tea.price(); } } class JinJu extends Decorator{ private Tea tea; public JinJu(Tea tea) { super(); this.tea = tea; } @Override public String getName() { return "金桔" + this.tea.getName(); } @Override public int price() { return 2+ this.tea.price(); } }
列印結果:
您點的是:奶茶 價格為:5
您點的是:檸檬茶 價格為:3
您點的是:奶茶 價格為:5
您點的是:珍珠奶茶 價格為:7
您點的是:椰果珍珠奶茶 價格為:9
您點的是:紅豆椰果珍珠奶茶 價格為:11
您點的是:金桔紅豆椰果珍珠奶茶 價格為:13
模式要點
定義:
動態的給物件新增一些額外的職責,就增加功能來說,裝飾者模式比起生成子類更加靈活!
四個角色:
- Component:抽象元件,可以是介面或抽象類,具體元件與抽象裝飾類的共同父類,聲明瞭在具體元件中實現的業務方法,可以使客戶端以一致的方式處理未修飾物件與修飾後的物件,實現了客戶端的透明操作,比如這裡的Tea類。
- ConcreteComponent:具體元件,實現抽象元件中生命的方法,裝飾器類可以給他增加額外的責任(方法),比如這裡的MilkTea和LemonTea。
- Decorator:抽象裝飾類,裝飾元件物件的,內部一定要有一個指向元件物件的引用!!!通過該引用可以呼叫裝飾前構建物件的方法,並通過其子類擴充套件該方法,已達到裝飾的目的,比如這裡的Decorator類。
- ConcreteDecorator:具體裝飾類,抽象裝飾類的具體實現,可以呼叫抽象裝飾類中定義的方法,也可以新增新的方法來擴充物件的行為。
UML類圖
適用場景
裝飾者模式是以對客戶端透明的方式擴充套件物件的功能,是繼承關係的一種替代方案! 以下情況可以考慮是想用物件組合(組合與委託):在不影響其他物件的情況下,以動態、透明的方式給單個物件新增職責;處理那些可以撤消的職責;當不能採用生成子類的方法進行擴充時:一種情況是,可能有大量獨立的擴充套件, 為支援每一種組合將產生大量的子類,使得子類數目呈爆炸性增長。另一種情況可能是因為類定義被隱藏,或類定義不能用於生成子類;
優缺點
- 擴充套件物件功能,比繼承靈活,不會導致類個數急劇增加;
- 可以通過一種動態的方式在執行時選擇不同的具體裝飾類,從而實現不同的行為;
- 避免了高層次類有太多的特徵,可以從一個最簡單的類慢慢給他新增功能;
- 會產生很多小裝飾者物件,會影響效能,過多使用該模式也會使程式變得複雜。