?裝飾,字面意思是對生活用品或生活環境進行藝術加工的手法。它必須與所裝飾的客體有機地結合,成為統一、和諧的整體,以便豐富藝術形象,擴大藝術表現力,加強審美效果,并提高其功能、經濟價值和社會效益。我們編程世界中的裝飾又有著怎樣與眾不同的解釋呢?原來裝飾模式是在不必改變原類文件和使用繼承的情況下,動態地擴展一個對象的功能。它是通過創建一個包裝對象,也就是裝飾來包裹真實的對象。
我們來看一個具體的例子,經過一個上午的消耗,加上昨天晚上熬夜看《出彩中國人》,早上賴床,沒有去吃飯,那上午叫一個餓啊,于是發誓,再也不熬夜了,咳咳,到了晚上,拿起手機什么又都忘了,于是,想著趕緊下課,去吃飯,最好來一碗面條,來點兒辣椒再來點兒醋,那味道,呼呼流口水了,來到賣面條的地方,這個點兒來的人可真多啊,咱是好孩子,得排隊是不是,順便看一下標簽上有哪些面條,哇塞面條的種類可真多啊,比如有雪菜肉絲面條,西紅柿雞蛋面條,小雞蘑菇面條,聽,我前面的小姑娘要了一碗西紅柿雞蛋面條,緊接著一個男孩要了一碗小雞蘑菇面條,每個同學的選擇是不同的,也就是需求是各種各樣的,那么這種情況在我們的編程世界中如何實現呢,這個時候,排隊的我想到了繼承,如下圖所示:
? ? ? ??
這個時候如果我想要加醋和加辣椒的面條?我要怎么辦,可以通過繼承來實現擴展,但是這樣的設計有點兒笨笨的,于是一種新設計模式--裝飾模式就這樣橫空出世了,我們來看看裝飾模式的結構圖:
? ? ? ??
裝飾者模式呢,其實可以看做是一種在已有功能上動態添加新的功能的一種方式,在不用裝飾者模式的前提下,如果要在已有的功能上添加新功能,一般都是可以使用繼承的,但是,繼承的缺點呢,在上面的例子中也暴露的很明顯,同時,使用繼承的話,添加功能不是動態的,因為子類完全繼承了父類,而使用裝飾者模式的話,您可以在客戶端按照需求一個一個的包裝對象,通過包裝對象來添加新功能,這樣便實現了動態添加新功能,比如,我可以對 Component 通過 ConcreteDecoratorA 來包裝一個 State 狀態,或者是通過 ConcreteDecoratorB 來包裝一個新的行為(功能)Behavior ,以我們的面條例子為例,看看我們的程序是怎么實現的呢?
先來看一下Eat類:
? ? ? ?
~~~
using?System;???
namespace?Decorator???
{???
????public?abstract?class??Noodle???
????{???
????????///????
????????///?在抽象類中只定義了一個抽象接口???
????????///?然后可以通過這個抽象接口來給對象動態的添加功能???
????????///????
????????public?abstract?void?ShowNoodle();???
????}???
}??
~~~
然后就是一個Noodle類:
~~~
using?System;???
namespace?Decorator???
{???
????public?class?Noodle?:?Eat??
????{???
????????private?string?name;???
????????public?Noodle(string?name)???
????????{???
????????????this.name?=?name;???
????????}???
????????///????
????????///?給當前的對象添加一些功能???
????????///?比如這里就是指定了面條的名稱???
????????///?這里添加的功能是靜態添加的???
????????///????
????????public?override?void?ShowEat()???
????????{???
????????????Console.WriteLine("面條名稱為:{0}????",?this.name);???
????????}???
????}???
}??
~~~
下面再來看 DecoratorEat 類:
~~~
namespace?Decorator???
{???
????public?class?DecoratorEat:?Noodle??
????{???
????????///????
????????///?在裝飾類中必須要保存一個對于對象的引用???
??????
????????///????
????????protected?Eat?eat;???
????????public?DecoratorEat(Eat?eat)???
????????{???
????????????this.eat?=?eat;???
????????}???
????????public?override?void?ShowEat()???
????????{???
????????????if?(Eat?!=?null)???
????????????{???
?????????????????
????????????????Eat.ShowEat();???
????????????}???
????????}???
????}???
}??
~~~
還有就是裝飾類 Tomato:
~~~
using?System;???
namespace?Decorator???
{???
????public?class?Tomato?:?DecoratorEat??
????{???
????????
????????///????
????????///????
????????public?Tomato(Eat?eat)???
????????????:?base(eat)???
????????{???
????????}???
????????public?override?void?Showeat()???
????????{???
???????????//首先必須要調用父類的?ShowEat???
????????????base.ShowEat();???
????????????//然后下面就可以添加新功能了???
????????????Console.WriteLine("加西紅柿???");???
????????}???
????}???
}??
~~~
裝飾類雞蛋和豆皮的代碼跟上述裝飾類西紅柿雷同,再此不一一贅述,接下來,我們一起看看客戶端的代碼:
~~~
using?System;???
using?Decorator;???
namespace?DecoratorTest???
{???
????class?Program???
????{???
????????static?void?Main(string[]?args)???
????????{???
????????????Noodle?noodle?=?new?Noodle("面條");???
????????????//給面條加西紅柿,也就是使用西紅柿來裝飾面條???
????????????Tomato?tomato?=?new?Tomato(noodle);???
????????????//給加了西紅柿的面條加雞蛋,也就是使用加雞蛋來裝飾面條???
????????????Egg?egg?=?new?Egg(Tomato);???
????????????//顯示出當前面條的狀態???
????????????Egg.ShowNoodle();???
????????????Console.WriteLine();???
????????????noodle?=?new?Noodle("面條");???
????????????tomato?=?new?Tomato(noodle);???
????????????//給加了西紅柿的面條加豆皮???
????????????Doupi?doupi?=?new?Doupi(Tomato);???
????????????//給加了西紅柿和雞蛋的面條加個豆皮???
????????????Doupi?doupi?=?new?Doupi(doupi);???
????????????doupi.ShowEat();???
????????????Console.ReadLine();???
????????}???
????}???
}??
~~~
通過裝飾模式我們的小菜有著百搭的風格,而我也.......嘻嘻,再回到我們的裝飾模式中來,裝飾模式與繼承關系的目的都是要擴展對象的功能,但是Decorator可以提供比繼承更多的靈活性。 通過使用不同的具體裝飾類以及這些裝飾類的排列組合,設計師可以創造出很多不同行為的組合。設計之旅,未完待續......? ??