裝飾者模式

1、基本概述編程

問題:有咖啡店,賣多種咖啡,以及調料(如豆漿、牛奶、奶油等)。現有的訂單系統的類結構以下。ide

2、分析說明this

上面的設計方式存在的一些問題?spa

  1. 調料價錢的改變會使咱們更改現有代碼。
  2. 一旦出現新的調料,咱們就須要加上新的方法(屬性),並改變超類中的Cost()方法。
  3. 之後可能會開發出新飲料,對這些飲料而言(例如,茶),某些調料可能並不適合,可是在這個設計方式中,Tea(茶)子類仍將繼承那些不適合的方法。例如,HasWhip(加奶泡)。
  4. 如果須要雙倍摩卡咖啡,怎麼辦。
  5. 違反了OO原則,如針對接口編程,而非針對實現編程。多用組合,少用繼承。

小結:設計

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());
}
View Code

------------------------以上內容根據《Head First Design mode》進行整理

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息