裝飾者模式動態的將責任附加到對象上,若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案。java
從定義來看,除了提到比繼承更有彈性,其餘的仍是很是模糊,下面就先給出其UML類圖。程序員
從UML類圖能夠看到裝飾者基類(Decorator)主要使用了一個其基類的組合,另外裝飾者分別含有本身的新特性,如ConcreteDecoratorA,有本身特有的字段,ConcreteDecoratorB有本身特有的方法。該模式設計的巧妙的地方就在於以上的特色。算法
爲何說巧妙呢?由於裝飾者都含有一個Component的引用,就比如你是老闆要找c#開發的,遇到了一個不只會c#的,還會java的你同樣,這樣的好處是無可置疑的,不只可讓你作asp.net,若是哪一天公司要開發andriord了,你照樣能夠勝任,而不須要再去找andriord的童鞋了,固然也多是不只具備C#技能,還多是能說會道的你。若是哪一天本身想跑業務,本身隨時均可以去。跑累了,想體驗一下代碼生活,照樣繼續幹代碼工做……在這個過程當中,就像本身被知識武裝了起來,因此叫作裝飾者模式。c#
固然有老闆要找個什麼都會的,所有集中在了一我的身上,也有必定的缺陷,或許一會讓你搞andriord,一會讓你去搞c#,一會讓你跑業務,這樣可能會有影響的。敲代碼剛剛有思路,就讓你去跑業務,顯然是有些不科學。設計模式
既要武裝本身,又要不能老闆不時地打擾你的思路,這就須要一個原則:asp.net
類應該對擴展開放,對修改關閉。ide
就是要鼓勵學習新東西,可是要禁止打斷寫代碼的思路。這樣才能提升效率,那人手不夠怎麼辦,找個新人。學習
說了這麼多,仍是以爲經過一個例子才能更好的說明。測試
下面就根據head first設計模式書中的例子,來看看冷飲店是如何賣飲料的。this
剛剛開店,只賣礦泉水(MineralWater)和咖啡(Coffee),價錢直接就調用一下其cost()方法就能夠的出來。但是聰明的老闆必定會想到若是加一下白糖(Sugar)或者牛奶(Milk)是否是能夠多賺些呢?可是算帳很差算。不可能讓礦泉水和咖啡中添加兩個方法,AddSugar和AddMilk,這樣會產生的問題,若是再加調料,會繼續修改飲料的類,這樣好不容易寫好的類,可能就要繼續就改,總共的價格可能也要修改。實在不是好辦法。
下面就來看看裝飾者模式怎麼處理這樣的問題。
若是你既是c#程序員,也是andriord程序員,老闆說了,我不打斷你的思路,白天你能夠作C#項目,晚上作andriord的項目,工資的算法你說了算。
你說晚上是白天的2倍,老闆說好,財務說,我只看你晚上有沒有幹,晚上含有白天的引用,計算方法:WageofNight()+Day.wageOfDay()。
一樣的道理咖啡店也會經過這樣的方式,調料引用一個飲料,總價格經過飲料的價錢加上引用調料的價格。
好了,上圖了。咖啡店中飲料類圖以下:
感受不上代碼,仍是會以爲少些什麼,下面就給出對應的代碼:
public abstract class Beverage { public string description = "全部飲料的基類"; public abstract double Cost(); public virtual string GetDesciption() { return description; } }
public class Coffee : Beverage { public Coffee() { description = "我是熱咖啡"; } public override double Cost() { return 1.50; } }
public class MineralWater : Beverage { public MineralWater() { description = "我是礦泉水"; } public override double Cost() { return 1.0; } }
public abstract class BeverageDecrator : Beverage { public Beverage beverage; public abstract string GetDesciption(); }
public class Milk : BeverageDecrator { public Milk(Beverage beverage) { this.beverage = beverage; } public override string GetDesciption() { return beverage.description + "-------加牛奶"; } public override double Cost() { return beverage.Cost() + 0.5; } }
public class Sugar : BeverageDecrator { public Sugar(Beverage beverage) { this.beverage = beverage; } public override string GetDesciption() { return beverage.description + "--------加糖"; } public override double Cost() { return 0.5 + beverage.Cost(); } }
測試代碼:
class Program { static void Main(string[] args) { Console.WriteLine("小二哥,來一杯礦泉水加糖"); Beverage mineralWater = new MineralWater(); Sugar sugar = new Sugar(mineralWater); Console.WriteLine("客官,來了,請慢用"); Console.WriteLine(sugar.GetDesciption()); Console.WriteLine("小二哥,結帳"); Console.WriteLine("客官,共"+sugar.Cost()+"¥"); Console.WriteLine("客官慢走"); Console.WriteLine("----------------------------"); Console.WriteLine("小二哥,來一杯咖啡加牛奶"); Beverage coffee = new Coffee(); Milk milk = new Milk(coffee); Console.WriteLine("客官,來了,請慢用"); Console.WriteLine(milk.GetDesciption()); Console.WriteLine("小二哥,結帳"); Console.WriteLine("客官,共" + milk.Cost() + "¥"); Console.WriteLine("客官慢走"); Console.ReadKey(); } }
請注意上述代碼是如何使用組合從而達到能夠自由的擴展以及修改的,最終的能夠互不影響的擴展和修改。爲了進一步演示自由的擴展和修改的,進一步提需求:如今我以爲加牛奶要貴些,而後送一張優惠券。
其餘的不改變,只改變Milk類,便可。
public class Milk : BeverageDecrator { public Milk(Beverage beverage) { this.beverage = beverage; } public override string GetDesciption() { return beverage.description + "-------加牛奶"; } public override double Cost() { SendCoupon(); return beverage.Cost() + 2; } public void SendCoupon() { Console.WriteLine("送優惠券一張"); } }
輸出結果:
看看如今修改了牛奶類,沒有改變其餘地方,就能夠獲得咱們想要的結果了。從而實現了前面所說的原則,開閉原則。
上面的設計好像是遵循了開閉原則,其實則沒有,由於類要對擴展開放,對修改關閉,咱們很明顯,對修改沒有關閉,只是對擴展開放而已,如今就來解決修改關閉的問題,要到達這樣的效果,就肯定了不能改變類的任何地方,只能對類進行擴展,是經過另一個類,來進行對改類的擴展,這樣作的目的是不只擴展了想要擴展的類,還能夠靈活的擴展本身想擴展的類。已達到複用的效果。接下來,看看如何在不改變原有的基礎上進行擴展,從而遵循開閉原則。
注意上面的BeverageExtend類,其類多了個SendCoupon方法,(這個方法也是爲了顯眼而已),用來送優惠券,咱們如今修改的是sugar類或Milk類,可是經過的是BeverageExtend類來擴展的,這樣纔是真正到達了開閉原則。
下面給出擴展類和測試代碼
public class BeverageExtend : BeverageDecrator { public BeverageExtend(Beverage beverage) { this.beverage = beverage; } public virtual void SendCoupon() { Console.WriteLine("總價爲" + this.Cost()); Console.WriteLine("送您一張優惠券"); } public override string GetDesciption() { return beverage.GetDesciption() + "——————加優惠券"; } public override double Cost() { return beverage.Cost() + 1.5; } }
測試代碼:
static void Main(string[] args) { Console.WriteLine("小二哥,來一杯礦泉水加糖"); MineralWater mineralWater = new MineralWater(); Sugar sugar = new Sugar(mineralWater); Console.WriteLine("客官,來了,請慢用"); Console.WriteLine(sugar.GetDesciption()); Console.WriteLine("小二哥,結帳"); Console.WriteLine("客官,共"+sugar.Cost()+"¥"); Console.WriteLine("客官慢走"); Console.WriteLine("----------------------------"); Console.WriteLine("小二哥,來一杯礦泉水加糖"); MineralWater mineralWater1 = new MineralWater(); Sugar sugar1 = new Sugar(mineralWater1); BeverageExtend beverage = new BeverageExtend(sugar1); Console.WriteLine("客官,來了,請慢用"); Console.WriteLine(beverage.GetDesciption()); Console.WriteLine("小二哥,結帳"); Console.WriteLine("客官,共" + beverage.Cost() + "¥"); Console.WriteLine("客官慢走"); Console.WriteLine("----------------------------"); Console.WriteLine("小二哥,來一杯咖啡加牛奶"); Coffee coffee = new Coffee(); Milk milk = new Milk(coffee); BeverageExtend beverage2 = new BeverageExtend(milk); Console.WriteLine("客官,來了,請慢用"); Console.WriteLine(beverage2.GetDesciption()); Console.WriteLine("小二哥,結帳"); Console.WriteLine("客官,共" +beverage2.Cost() + "¥"); Console.WriteLine("客官慢走"); Console.ReadKey(); }
下面給出測試結果圖:
本文主要經過裝飾者模式,引出了設計模式中的開閉原則,經過冷飲店的售價,來體現裝飾者如何實現開閉原則的,可是須要注意要對類進行擴展是經過另一個裝飾類來完成的,而不是經過在自己類中實現的。