今天咱們要講【結構型】設計模式的第三個模式,該模式是【裝飾模式】,英文名稱:Decorator Pattern。我第一次看到這個名稱想到的是另一個詞語「裝修」,我就說說我對「裝修」的理解吧,你們必定要看清楚,是「裝修」,不是「裝飾」。咱們長大了,就要結婚,要結婚就涉及到要買房子,買的精裝修或者簡單裝修就能夠住的,暫時不談。咱們就談談咱們購買的是毛坯房。若是我想要房子的內飾是大理石風格的,咱們只要在毛坯房的基礎之上用大理石風格的材料裝修就能夠,咱們固然不可能爲了要一個裝修風格,就把剛剛蓋好的房子拆了在從新來過。房子裝修好了,咱們就住了進來,很開心。過了段時間,咱們發現咱們的房子在冬季比較冷,因而我就想給咱們的房子增長保暖的功能,裝修好的房子咱們能夠繼續居住,咱們只是在房子外面加一層保護層就能夠了。又過了一段時間,老是有陌生人光顧,因此咱們想讓房子更安全,因而咱們在外牆和房頂加裝安全攝像頭,同時門窗也增長安全系統。隨着時間的流逝,咱們可能會根據咱們的需求增長相應的功能,期間,咱們的房子能夠正常使用,加上什麼設施就有了相應的功能。從這一方面來說,「裝修」和「裝飾」有相似的概念,接下來就讓咱們看看裝飾模式具體是什麼吧!
2、裝飾模式的詳細介紹
2.一、動機(Motivate)
在房子裝修的過程當中,各類功能能夠相互組合,來增長房子的功用。相似的,若是咱們在軟件系統中,要給某個類型或者對象增長功能,若是使用「繼承」的方案來寫代碼,就會出現子類暴漲的狀況。好比:IMarbleStyle是大理石風格的一個功能,IKeepWarm是保溫的一個接口定義,IHouseSecurity是房子安全的一個接口,就三個接口來講,House是咱們房子,咱們的房子要什麼功能就實現什麼接口,若是房子要的是複合功能,接口不一樣的組合就有不一樣的結果,這樣就致使咱們子類膨脹嚴重,若是須要在增長功能,子類會成指數增加。這個問題的根源在於咱們「過分地使用了繼承來擴展對象的功能」,因爲繼承爲類型引入的靜態特質(所謂靜態特質,就是說若是想要某種功能,咱們必須在編譯的時候就要定義這個類,這也是強類型語言的特色。靜態,就是指在編譯的時候要肯定的東西;動態,是指運行時肯定的東西),使得這種擴展方式缺少靈活性;而且隨着子類的增多(擴展功能的增多),各類子類的組合(擴展功能的組合)會致使更多子類的膨脹(多繼承)。如何使「對象功能的擴展」可以根據須要來動態(即運行時)地實現?同時避免「擴展功能的增多」帶來的子類膨脹問題?從而使得任何「功能擴展變化」所致使的影響降爲最低?
2.二、意圖(Intent)
動態地給一個對象增長一些額外的職責。就增長功能而言,Decorator模式比生成子類更爲靈活。 —— 《設計模式》GoF
2.三、結構圖(Structure)
2.四、模式的組成
在裝飾模式中的各個角色有:
(1)、抽象構件角色(Component):給出一個抽象接口,以規範準備接收附加責任的對象。
(2)、具體構件角色(Concrete Component):定義一個將要接收附加責任的類。
(3)、裝飾角色(Decorator):持有一個構件(Component)對象的實例,並實現一個與抽象構件接口一致的接口。
(4)、具體裝飾角色(Concrete Decorator):負責給構件對象添加上附加的責任。
2.5 、裝飾模式的具體代碼實現
剛開始一看這個「裝飾模式」是有點不太好理解,既然這個模式是面向對象的設計模式,那在現實生活中必定有事例和其對應,其實這種例子也很多,你們好好的挖掘吧,也能夠提升咱們對面向對象的理解。我繼續拿蓋房子來講事吧。
javascript
1 namespace 裝飾模式的實現 2 { 3 /// <summary> 4 /// 該抽象類就是房子抽象接口的定義,該類型就至關因而Component類型,是餃子餡,須要裝飾的,須要包裝的 5 /// </summary> 6 public abstract class House 7 { 8 //房子的裝修方法--該操做至關於Component類型的Operation方法 9 public abstract void Renovation(); 10 } 11 12 /// <summary> 13 /// 該抽象類就是裝飾接口的定義,該類型就至關因而Decorator類型,若是須要具體的功能,能夠子類化該類型 14 /// </summary> 15 public abstract class DecorationStrategy:House //關鍵點之二,體現關係爲Is-a,有這這個關係,裝飾的類也能夠繼續裝飾了 16 { 17 //經過組合方式引用Decorator類型,該類型實施具體功能的增長 18 //這是關鍵點之一,包含關係,體現爲Has-a 19 protected House _house; 20 21 //經過構造器注入,初始化平臺實現 22 protected DecorationStrategy(House house) 23 { 24 this._house=house; 25 } 26 27 //該方法就至關於Decorator類型的Operation方法 28 public override void Renovation() 29 { 30 if(this._house!=null) 31 { 32 this._house.Renovation(); 33 } 34 } 35 } 36 37 /// <summary> 38 /// PatrickLiu的房子,我要按個人要求作房子,至關於ConcreteComponent類型,這就是咱們具體的餃子餡,我我的比較喜歡韭菜餡 39 /// </summary> 40 public sealed class PatrickLiuHouse:House 41 { 42 public override void Renovation() 43 { 44 Console.WriteLine("裝修PatrickLiu的房子"); 45 } 46 } 47 48 49 /// <summary> 50 /// 具備安全功能的設備,能夠提供監視和報警功能,至關於ConcreteDecoratorA類型 51 /// </summary> 52 public sealed class HouseSecurityDecorator:DecorationStrategy 53 { 54 public HouseSecurityDecorator(House house):base(house){} 55 56 public override void Renovation() 57 { 58 base.Renovation(); 59 Console.WriteLine("增長安全系統"); 60 } 61 } 62 63 /// <summary> 64 /// 具備保溫接口的材料,提供保溫功能,至關於ConcreteDecoratorB類型 65 /// </summary> 66 public sealed class KeepWarmDecorator:DecorationStrategy 67 { 68 public KeepWarmDecorator(House house):base(house){} 69 70 public override void Renovation() 71 { 72 base.Renovation(); 73 Console.WriteLine("增長保溫的功能"); 74 } 75 } 76 77 public class Program 78 { 79 static void Main() 80 { 81 //這就是咱們的餃子餡,須要裝飾的房子 82 House myselfHouse=new PatrickLiuHouse(); 83 84 DecorationStrategy securityHouse=new HouseSecurityDecorator(myselfHouse); 85 securityHouse.Renovation(); 86 //房子就有了安全系統了 87 88 //若是我既要安全系統又要保暖呢,繼續裝飾就行 89 DecorationStrategy securityAndWarmHouse=new HouseSecurityDecorator(securityHouse); 90 securityAndWarmHouse.Renovation(); 91 } 92 } 93 }
寫了不少備註,你們好好體會一下,裏面有兩個關鍵點,仔細把握。
3、裝飾模式的實現要點:
一、經過採用組合、而非繼承的手法,Decorator模式實現了在運行時動態地擴展對象功能的能力,並且能夠根據須要擴展多個功能。避免了單獨使用繼承帶來的「靈活性差」和「多子類衍生問題」。
二、Component類在Decorator模式中充當抽象接口的角色,不該該去實現具體的行爲。並且Decorator類對於Component類應該透明——換言之Component類無需知道Decorator類,Decorator類是從外部來擴展Component類的功能。
三、Decorator類在接口上表現爲is-a Component的繼承關係,即Decorator類繼承了Component類所具備的接口。但在實現上又表現爲has-a Component的組合關係,即Decorator類又使用了另一個Component類。咱們可使用一個或者多個Decorator對象來「裝飾」一個Component對象,且裝飾後的對象仍然是一個Component對象。
四、Decorator模式並不是解決「多子類衍生的多繼承」問題,Decorator模式應用的要點在於解決「主體類在多個方向上的擴展功能」——是爲「裝飾」的含義。
3.1】、裝飾模式的優勢:
(1)、把抽象接口與其實現解耦。
(2)、抽象和實現能夠獨立擴展,不會影響到對方。
(3)、實現細節對客戶透明,對用於隱藏了具體實現細節。
3.2】、裝飾模式的缺點:
(1)、增長了系統的複雜度
3.3】、在如下狀況下應當使用橋接模式:
(1)、若是一個系統須要在構件的抽象化角色和具體化角色之間添加更多的靈活性,避免在兩個層次之間創建靜態的聯繫。
(2)、設計要求實現化角色的任何改變不該當影響客戶端,或者實現化角色的改變對客戶端是徹底透明的。
(3)、須要跨越多個平臺的圖形和窗口系統上。
(4)、一個類存在兩個獨立變化的維度,且兩個維度都須要進行擴展。
4、.NET 中裝飾模式的實現
在Net框架中,有一個類型很明顯的使用了「裝飾模式」,這個類型就是Stream。Stream類型是一個抽象接口,它在System.IO命名空間裏面,它其實就是Component。FileStream、NetworkStream、MemoryStream都是實體類ConcreteComponent。右邊的BufferedStream、CryptoStream是裝飾對象,它們都是繼承了Stream接口的。java
如圖:
設計模式
Stream就至關於Component,定義裝飾的對象,FileStream就是要裝飾的對象,BufferedStream是裝飾對象。咱們看看BufferedStream的定義,部分定義了。安全
1 public sealed class BufferedStream : Stream 2 { 3 private const int _DefaultBufferSize = 4096; 4 5 private Stream _stream;
結構很簡單,對比結構圖看吧,沒什麼可說的了。框架