【原】從頭學(xué)習(xí)設(shè)計模式(五)——裝飾者模式

一、引入
當(dāng)我們想要擴(kuò)展類的功能的時候,很多情況下會考慮用繼承的方法,比如我有一個手機類,只支持打電話,如果我們想要擴(kuò)展手機的功能,讓普通的手機變成智能手機,那最簡單的方式就是新建一個智能手機類并繼承手機類,擴(kuò)充智能手機擁有的新功能,比如打飛機,玩憤怒的小鳥之類的。
但是子類繼承的方法總歸不是非常靈活啊,為了更好的解決類功能擴(kuò)展的問題,我們來引用今天要學(xué)習(xí)的新模式——裝飾模式。首先先看一下標(biāo)準(zhǔn)定義。
裝飾模式(Decorator),動態(tài)地給一個對象添加一些額外的職責(zé),就增加功能來說,裝飾模式比生成子類更靈活。
從這個定義中可以找到幾個要點:
1.動態(tài)的擴(kuò)展
2.不會改變原有類
3.不用生成子類的繼承方式
在這個模式中主要有四大組件
1.原始類接口(接口類型或者抽象類)
2.原始類的具體實現(xiàn)類
3.裝飾器接口(一般類或抽象類)
4.繼承了裝飾器接口的一系列具體裝飾器
對照以上這些組件來看一下類圖表示。
二、UML類圖

從UML中可以看到:具體實現(xiàn)類和裝飾器基類都繼承自原始類接口
下面我們來模擬一下將一部普通手機升級為智能手機和山寨智能手機的全過程。
三、代碼描述
1.原始類接口可以是interface或者abstract class
1 /// <summary> 2 /// 手機總接口 3 /// </summary> 4 public interface Phone 5 { 6 void Functionality(); 7 }
2.原始類的具體實現(xiàn)類
1 /// <summary> 2 /// 一般的手機類 3 /// </summary> 4 public class CommonPhone : Phone 5 { 6 public void Functionality() 7 { 8 Console.WriteLine("能發(fā)短信和打電話."); 9 } 10 }
3. 裝飾器接口,可以是普通類(用virtual聲明 Functionality() 方法可以被子類重寫)或者抽象類(override Functionality() 方法)
1 /// <summary> 2 /// 裝飾基類 3 /// </summary> 4 public class PhoneDecorator : Phone 5 { 6 protected Phone phone; 7 8 public PhoneDecorator() 9 { 10 } 11 12 public PhoneDecorator(Phone phone) 13 { 14 this.phone = phone; 15 } 16 17 public virtual void Functionality() 18 { 19 if (phone != null) 20 { 21 phone.Functionality(); 22 } 23 } 24 }
4.具體裝飾器,需要重寫基類Functionality() 方法,并添加自己的新功能
1 /// <summary> 2 /// 智能機修飾類 3 /// </summary> 4 public class AdvancedPhone : PhoneDecorator 5 { 6 public AdvancedPhone(Phone phone) 7 : base(phone) 8 { 9 10 } 11 12 public override void Functionality() 13 { 14 base.Functionality(); 15 Console.WriteLine("可以玩憤怒的小鳥!"); 16 } 17 18 }
1 /// <summary> 2 /// 山寨機裝飾類 3 /// </summary> 4 public class ShanZhaiPhone : PhoneDecorator 5 { 6 public ShanZhaiPhone(Phone phone) 7 : base(phone) 8 { 9 } 10 11 public override void Functionality() 12 { 13 base.Functionality(); 14 Console.WriteLine("可以雙卡雙待!"); 15 } 16 }
5.功能測試類:
1 private static void Main(string[] args) 2 { 3 Console.WriteLine("-----------------------------普通手機-------------------------------"); 4 Phone commonPhone = new CommonPhone(); 5 commonPhone.Functionality(); 6 7 Console.WriteLine("-----------------------------智能手機-------------------------------"); 8 //將普通手機包一層智能的皮就成了智能手機 9 AdvancedPhone advancedPhone = new AdvancedPhone(commonPhone); 10 advancedPhone.Functionality(); 11 12 Console.WriteLine("-----------------------------山寨手機-------------------------------"); 13 //將普通手機包一層山寨的皮就成了強大的山寨手機 14 ShanZhaiPhone shangZhaiPhone = new ShanZhaiPhone(commonPhone); 15 shangZhaiPhone.Functionality(); 16 17 Console.WriteLine("-----------------------------山寨智能手機-------------------------------"); 18 //將智能手機包一層山寨的皮就成了山寨智能機 19 ShanZhaiPhone shangZhaiAdvancedPhone = new ShanZhaiPhone(new AdvancedPhone(new CommonPhone())); 20 shangZhaiAdvancedPhone.Functionality(); 21 22 Console.ReadKey(); 23 24 }
運行結(jié)果:

從這個小例子里可以看得出來,裝飾模式其實就是一個包裝器,并且是從里到外一層一層的包,是有先后順序的。同時,各種不同裝飾器的組合,可以組合出完全不同的效果來。這種鏈?zhǔn)降陌绞接纸凶鳌?nbsp;裝飾鏈。
你可以想想Windows中的字體設(shè)定——裝飾模式 ,這就是一個模式應(yīng)用的好例子,你可以對字體加粗,傾斜,改字體,改字號,所有單獨的功能包裹到一起就合成了這樣的字體。
四、總結(jié)
裝飾模式是一個為已有功能的類動態(tài)添加更多功能的一種方式,各個功能的擴(kuò)展相互獨立,可以根據(jù)需要隨意組合,但組合的順序有先后。
優(yōu)點:
1.有效避免了繼承方式對擴(kuò)展對象功能帶來的靈活性差的問題
2.通過組合而非繼承的方式,實現(xiàn)了動態(tài)擴(kuò)展對象的功能的能力
3.符合高內(nèi)聚、低耦合的設(shè)計思想
缺點:
1.裝飾鏈不能過長,否則影響效率
2.原始類接口不能改變,因為所有裝飾器基類繼承于原始類接口,類接口變化會導(dǎo)致所有裝飾器的修改


浙公網(wǎng)安備 33010602011771號