裝飾者模式 Decorator

項目:咖啡計費系統編程

背景:現有系統中有一個抽象類Beverage,有2個抽象方法GetDescription和Cost。設計模式

 1 namespace DecoratorPattern
 2 {
 3     /// <summary>
 4     /// 飲料抽象類
 5     /// </summary>
 6     public abstract  class Beverage
 7     {
 8         protected string description = "飲料";
 9         protected float price = 0f;
10         public abstract string GetDescription();
11         
12         public abstract float Cost();
13     }
14 }

 

需求:目前有綜合咖啡、深焙咖啡、濃縮咖啡,調料有牛奶、摩卡、豆漿、奶泡。將來可能增長新的咖啡種類和調料,當顧客點咖啡時,要求可以得到咖啡的描述和價格。ide

設計方案1:設計綜合咖啡、深焙咖啡、濃縮咖啡4個子類,繼承Beverage。再用這4個子類分別派生4個子類,帶有牛奶的綜合咖啡,帶有摩卡的綜合咖啡,帶有豆漿的綜合咖啡...this

分析:缺點時顯而易見的,這樣作致使「類爆炸」,一共須要3*4=12個子類。spa

設計方案2:把調料做爲咖啡的屬性設置在Beverage裏,並增長方法HasMilk(),SetMilk()等相似的方法。設計

分析:缺點這樣作無疑時從一個災難跳進另外一個災難中。咱們在開發中應當儘可能避免修改已有代碼,遵循「開閉原則」。並且當增長新的飲料時,又要修改基類。code

另外一個災難是,子類在計算價格時,須要大量的分支結構來判斷是否包含某種調料,以計算咖啡的價格,咱們老是儘可能的避免複雜的分支結構,這使得維護變得很是困難。對象

還有針對實現編程帶來的問題,不可以動態的添加職責。blog

裝飾者模式 Decorator 閃亮登場:繼承

裝飾者模式動態的將職責附加到對象上。若要擴展共呢個,裝飾者提供了比繼承更有彈性的替代方案。

1. 4個基類繼承Beverage

 1 namespace DecoratorPattern
 2 {
 3     /// <summary>
 4     /// 綜合咖啡
 5     /// </summary>
 6     public class HouseBlend:Beverage
 7     {
 8         public HouseBlend(float price)
 9         {
10             this.price = price;
11             this.description = "綜合咖啡";
12         }
13 
14         public override float Cost()
15         {
16             return price;
17         }
18         public override string GetDescription()
19         {
20             return this.description;
21         }
22     }
23 }
 1 namespace DecoratorPattern
 2 {
 3     /// <summary>
 4     /// 深焙咖啡
 5     /// </summary>
 6     public class DarkRoast:Beverage
 7     {
 8         public DarkRoast(float price)
 9         {
10             this.price = price;
11             this.description = "深焙咖啡";
12         }
13         public override string GetDescription()
14         {
15             return this.description;
16         }
17         public override float Cost()
18         {
19             return price;
20         }
21     }
22 }
 1 namespace DecoratorPattern
 2 {
 3     /// <summary>
 4     /// 濃縮咖啡
 5     /// </summary>
 6     public class Espresso:Beverage
 7     {
 8         public Espresso(float price)
 9         {
10             this.price = price;
11             this.description = "濃縮咖啡";
12         }
13         public override float Cost()
14         {
15             return this.price;
16         }
17         public override string GetDescription()
18         {
19             return this.description;
20         }
21     }
22 }

裝飾者繼承Beverage,注意這裏繼承的目的並非爲了得到基類的功能,而是爲了類型匹配,達到多態的目的,得到功能由組合來實現。所以每個裝飾者都須要維護一個Beverage引用。

 1 namespace DecoratorPattern
 2 {
 3     /// <summary>
 4     /// 摩卡裝飾者,爲了可以取代Beverage,因此CondimentDecorator繼承自Beverage,目的並不是得到Beverage
 5     /// 而是爲了類型匹配
 6     /// </summary>
 7     public class Mocha : Beverage
 8     {
 9         //持有抽象類飲料的引用,達到運行時添加職責的目的
10         Beverage beverage;
11         //包裝Beverage
12         public Mocha(Beverage b, float price)
13         {
14             beverage = b;
15             this.price = price;
16         }
17         public override float Cost()
18         {
19             return beverage.Cost() + price;
20         }
21 
22         public override string GetDescription()
23         {
24             return beverage.GetDescription() + ", 摩卡";
25         }
26     }
27 }
 1 namespace DecoratorPattern
 2 {
 3     /// <summary>
 4     /// 奶泡裝飾者
 5     /// </summary>
 6     public class Whip : Beverage
 7     {
 8         private Beverage beverage;
 9         public Whip(Beverage b, float price)
10         {
11             beverage = b;
12             this.price = price;
13         }
14         public override float Cost()
15         {
16             return beverage.Cost() + price;
17         }
18         public override string GetDescription()
19         {
20             return (beverage.GetDescription() + " ,奶泡");
21         }
22     }
23 }
 1 namespace DecoratorPattern
 2 {
 3     /// <summary>
 4     /// 豆漿裝飾者
 5     /// </summary>
 6     public class Soy : Beverage
 7     {
 8         private Beverage beverage;
 9         public Soy(Beverage b, float price)
10         {
11             this.price = price;
12             beverage = b;
13         }
14         public override float Cost()
15         {
16             return beverage.Cost() + price;
17         }
18 
19         public override string GetDescription()
20         {
21             return( beverage.GetDescription() + ", 豆漿");
22         }
23     }
24 }

客戶端類:CoffeeShop.cs

 1 using System;
 2 
 3 namespace DecoratorPattern
 4 {
 5     class CoffeeShop
 6     {
 7         static void Main(string[] args)
 8         {
 9             //來一杯濃縮咖啡,不要調料
10             Beverage beverage = new Espresso(1.99f);
11             Console.WriteLine(beverage.GetDescription() + "$" + beverage.Cost());
12 
13             //來一杯摩卡奶泡深焙咖啡
14             Beverage beverage2 = new DarkRoast(0.99f);
15             beverage2 = new Whip(beverage2, 0.1f);       //用奶泡裝飾深焙咖啡
16             beverage2 = new Mocha(beverage2, 0.2f);      //再用摩卡裝飾
17             Console.WriteLine(beverage2.GetDescription() + "$" + beverage2.Cost());
18 
19 
20             Console.ReadKey();
21         }
22 
23     }
24 }

運行結果:

參考資料《Head First 設計模式》

相關文章
相關標籤/搜索