1、基本概述編程
問題:有咖啡店,賣多種咖啡,以及調料(如豆漿、牛奶、奶油等)。現有的訂單系統的類結構以下。ide
2、分析說明this
上面的設計方式存在的一些問題?spa
小結:設計
1.儘管繼承的威力強大,可是它並不老是可以實現最有效彈性和最好維護的設計。code
2.利用繼承設計子類的行爲,是在編譯時靜態決定的,並且全部的子類都會繼承到相同的行爲。然而,若是可以利用組合的作法擴展對象的行爲,就能夠在運行時動態地進行擴展。對象
3.咱們能夠利用此技巧把多個新職責,甚至是設計超類時尚未想到的職責附加在對象上,並且能夠不用修改原來的代碼。blog
經過動態地組合對象,能夠寫新的代碼添加新功能,而無需修改現有代碼。既然沒有改變現有代碼,那麼引進bug或產生意外反作用的機會將大幅度減小。繼承
3、如何解決接口
1.OO原則:類應該對擴展開放,對修改關閉。(開放—關閉原則)
補充:
那麼咱們是否須要系統對設計的每一個部分都遵循開放—關閉原則呢?
答案是否認的。一般你辦不到,要讓OO設計同時具有開放性與關閉性,又不修改現有的代碼,須要花費許多時間和努力。通常來講,咱們實在沒有閒工夫把設計的每一個部分都這麼設計(並且,就算作獲得,也多是一種浪費)。遵循開放—關閉原則,一般會引入新的抽象層次,增長代碼的複雜度,你須要把注意力集中在設計中最有可能改變的地方,而後應用開放—關閉原則。
偏偏裝飾者模式符合這一原則。
2.裝飾者模式動態地將責任附加到對象上。若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案。
補充:
(1) 裝飾者和被裝飾者對象有相同的超類型。
(2) 你能夠用一個或多個裝飾者包裝一個對象。
(3) 既然裝飾者和被裝飾者對象有相同的超類型,因此在任何須要原始對象(被包裝對象)的場合,能夠用裝飾者對象代替它。
(4) 裝飾者能夠在所委託的被裝飾者的行爲以前與/或以後,加上本身的行爲,以達到特定的目地。
(5) 對象能夠在任什麼時候候被裝飾,因此能夠在運行時動態地、不限量地用裝飾者來裝飾對象。
上面雖說明了裝飾者模式的「角色」,可是沒有說明在具體的實際中怎麼使用,下面的類圖結構可以幫咱們梳理思路,後面的飲料問題就是套用此結構圖。
看了上面的類圖,是否在繼承和組合之間思惟有些混淆。
(1) 如CondimentDecorator擴展自Beverage類,這用到了繼承,不是嗎?
答:的確是如此,但我認爲這麼作的重點在於,裝飾者和被裝飾者必須是同樣的類型。也就是有共同的超類,這是至關關鍵的地方。在這裏,咱們利用繼承到達「類型匹配」,而不是利用繼承得到「行爲」。
(2) 既然裝飾者必須能代替被裝飾者,那行爲又從哪裏來呢?
答:當咱們將裝飾者與組件組合時,就是在加入新的行爲,所獲得的新行爲,並非繼承自超類,而是由組合對象得來的。
(3) 若是咱們須要繼承的是Component類型,爲何不把Beverage類設計成一個接口,而是設計成一個抽象類呢?
答;一般裝飾者模式是採用抽象類,可是也可使用接口。儘管如此,但咱們都努力避免修改現有的代碼。而這個程序的Beverage類自己一個抽象類。
下面列出飲料系統的詳細的類圖與代碼。
/// <summary> /// 飲料 /// </summary> public abstract class Beverage { protected string description = "Unknown Beverage"; public string Description { get { return description; } } public abstract float Cost(); } /// <summary> /// 抽象的調料裝飾者 /// </summary> public abstract class CondimentDecorator : Beverage { //TODO 可根據須要添加處理信息 } public class Espresso : Beverage { public Espresso() { description = "Espresso"; } public override float Cost() { return 1.99f; } } public class HouseBlend : Beverage { public HouseBlend() { description = "House Blend Coffee"; } public override float Cost() { return 0.89f; } } public class DarkRoast : Beverage { public DarkRoast() { description = "Dark Roast Coffee"; } public override float Cost() { return 0.99f; } } public class Decaf : Beverage { public Decaf() { description = "Decaf coffee"; } public override float Cost() { return 1.05f; } } public class Mocha : CondimentDecorator { private Beverage beverage; public Mocha(Beverage beverage) { this.beverage = beverage; this.description= beverage.Description + ",Mocha"; } public override float Cost() { return 0.20f + beverage.Cost(); } } public class Soy : CondimentDecorator { private Beverage beverage; public Soy(Beverage beverage) { this.beverage = beverage; this.description = beverage.Description + ",Soy"; } public override float Cost() { return 0.15f + beverage.Cost(); } } public class Whip : CondimentDecorator { private Beverage beverage; public Whip(Beverage beverage) { this.beverage = beverage; this.description= beverage.Description + ",Whip"; } public override float Cost() { return 0.10f + beverage.Cost(); } } [Test] public void StarbuzzCoffee() { Beverage beverage = new Espresso(); Console.WriteLine("{0} ${1}", beverage.Description, beverage.Cost()); Beverage beverage2 = new DarkRoast(); beverage2 = new Mocha(beverage2); beverage2 = new Mocha(beverage2); beverage2 = new Whip(beverage2); Console.WriteLine("{0} ${1}", beverage2.Description, beverage2.Cost()); Beverage beverage3 = new HouseBlend(); beverage3 = new Soy(beverage3); beverage3 = new Mocha(beverage3); beverage3 = new Whip(beverage3); Console.WriteLine("{0} ${1}", beverage3.Description, beverage3.Cost()); }
------------------------以上內容根據《Head First Design mode》進行整理