背景是有一家星巴茲咖啡店,因爲客源充足,因此決定從新設計他們的收費系統,之前的收費系統中只定義了一個表示飲料的Beverage的基類,它裏面定義了一個Cost的方法用來計算飲料的花費,可是對於星巴茲來講他們的飲料的種類實在太多了,不能就每一種飲料就創建一個子類,類型爆炸!編程
因此要進行一番設計,來改變目前這種類型爆炸的局面。ide
利用繼承設計子類的行爲,實在編譯時靜態決定的,並且全部的子類都會繼承到相同的行爲,若是可以利用組合的作法擴展對象的行爲,就能夠在運行時動態的進行擴展。經過動態的組合對象,能夠寫新的代碼添加新功能,而無須修改現有代碼。既然沒有改變現有代碼,那麼引進bug或者產生意外反作用的機會就會變得少不少。測試
設計原則:類應該對擴展開放,對修改關閉(開閉原則)this
咱們的目標是容許類容易擴展,在不修改現有代碼的狀況下,就可搭配新的行爲,若是實現這樣的目標,有什麼好處呢?這樣的設計具備彈性能夠應對改變,能夠接受新的功能來應對改變的需求。spa
遵循開放-關閉原則,一般會引入新的抽象層次,增長代碼的複雜度。須要把注意力集中在設計中最有可能改變的地方,而後應用開放-關閉原則。設計
按照上述原則分析需求,首先,咱們把飲料看成主體,而各類調料看成裝飾,用調料來裝飾飲料,若是顧客想要摩卡和奶泡咖啡,那麼要作的是:3d
①準備一個咖啡對象。code
②用摩卡裝飾他。對象
③用奶泡裝飾他。blog
④調用cost方法。
在給出具體的作法以前,先給出裝飾者模式的定義:動態的將責任附加到對象上,如要擴展功能,裝飾者提供了比繼承更有彈性的替代方案。
簡單的來講,就是用Decorator來裝飾ConcreteComponent,由於他們都具備相同的基類,達到了一種「類型匹配」的目的,而不是用來獲取基類的行爲。到時候,相關的類型之間可以被互相取代。
當咱們將裝飾着和組件組合時,就是在加入新的行爲,所獲得的新的行爲,並非繼承得來的,而是有組合對象得來的。若是依賴繼承,那麼類的行爲就是在編譯時靜態決定的。換句話說,行爲若是不是來自基類,就是來自子類覆蓋後的版本。反之,若是利用組合,能夠把裝飾着混合着用,並且是在運行時。
並且還能夠在任什麼時候候實現新的裝飾者增長新的行爲,若是依賴繼承,每當須要新行爲時,還得修改現有的代碼。
按照上面給的UML類圖,首先從Beverage下手,這表明了一個Component,全部的類型都直接或者間接的從它繼承,來獲取類型的一致性。而這是Decorator的關鍵。
public abstract class Beverage { public virtual string Description { get; set; } public abstract double Cost(); }
接着,定義一個ConcreteComponent:
public sealed class Espresso:Beverage//表明某種咖啡 { public Espresso() { Description = "Espresso"; } public override double Cost() { return 1.2D; } }
因爲在Beverage中Cost是抽象方法,因此要在子類中去override。
再定義一個裝飾者的基類,也就是類圖上的CondimentDecorator:
public abstract class CondimentDecorator : Beverage { public override string Description { get; set; } }
而後就是定義具體的裝飾類:
public class Mocha:CondimentDecorator//摩卡,一種咖啡的「裝飾」 { private readonly Beverage _beverage; public Mocha(Beverage beverage) { this._beverage = beverage; } public override string Description { get => _beverage.Description + "," + " Mocha"; set => base.Description = value; } public override double Cost() { return 1.5 + _beverage.Cost(); } }
而後測試一下:
class Program { static void Main(string[] args) { Beverage beverage=new Espresso(); Console.WriteLine($"{beverage.Description},Cost is {beverage.Cost()}"); beverage=new Mocha(beverage);//進一步裝飾 Console.WriteLine($"{beverage.Description},Cost is {beverage.Cost()}"); beverage=new Mocha(beverage);//進一步裝飾 Console.WriteLine($"{beverage.Description},Cost is {beverage.Cost()}"); Console.ReadKey(); } }
從上述代碼能夠看到這種裝飾的行爲正是依賴了統一的基類帶來的類型的一致性,也證明了咱們前面所言非虛。而在具體類的內部,根據對象的組合進一步擴展了對象的功能。因爲對象組合上是針對抽象(抽象類)進行編程,因此很容易經過IOC容器來進行控制反轉的實現。