首頁 > 軟體

.Net結構型設計模式之裝飾模式(Decorator)

2022-05-25 18:00:50

一、動機(Motivate)

在房子裝修的過程中,各種功能可以相互組合,來增加房子的功用。類似的,如果我們在軟體系統中,要給某個型別或者物件增加功能,如果使用“繼承”的方案來寫程式碼,就會出現子類暴漲的情況。比如:IMarbleStyle是大理石風格的一個功能,IKeepWarm是保溫的一個介面定義,IHouseSecurity是房子安全的一個介面,就三個介面來說,House是我們房子,我們的房子要什麼功能就實現什麼介面,如果房子要的是複合功能,介面不同的組合就有不同的結果,這樣就導致我們子類膨脹嚴重,如果需要在增加功能,子類會成指數增長。這個問題的根源在於我們“過度地使用了繼承來擴充套件物件的功能”,由於繼承為型別引入的靜態特質(所謂靜態特質,就是說如果想要某種功能,我們必須在編譯的時候就要定義這個類,這也是強型別語言的特點。靜態,就是指在編譯的時候要確定的東西;動態,是指執行時確定的東西),使得這種擴充套件方式缺乏靈活性;並且隨著子類的增多(擴充套件功能的增多),各種子類的組合(擴充套件功能的組合)會導致更多子類的膨脹(多繼承)。如何使“物件功能的擴充套件”能夠根據需要來動態(即執行時)地實現?同時避免“擴充套件功能的增多”帶來的子類膨脹問題?從而使得任何“功能擴充套件變化”所導致的影響降為最低?

二、意圖(Intent)

動態地給一個物件增加一些額外的職責。就增加功能而言,Decorator模式比生成子類更為靈活。        ——  《設計模式》GoF

三、結構圖(Structure)

四、模式的組成

在裝飾模式中的各個角色有:
(1)、抽象構件角色(Component):給出一個抽象介面,以規範準備接收附加責任的物件。
(2)、具體構件角色(Concrete Component):定義一個將要接收附加責任的類。
(3)、裝飾角色(Decorator):持有一個構件(Component)物件的範例,並實現一個與抽象構件介面一致的介面。
(4)、具體裝飾角色(Concrete Decorator):負責給構件物件新增上附加的責任。

五 、裝飾模式的具體程式碼實現

繼續拿蓋房子來說事吧。

/// <summary>
/// 該抽象類就是房子抽象介面的定義,該型別就相當於是Component型別,是餃子餡,需要裝飾的,需要包裝的
/// </summary>
public abstract class House
{
    public abstract void Renovation();//房子的裝修方法--該操作相當於Component型別的Operation方法
}

/// <summary>
/// 該抽象類就是裝飾介面的定義,該型別就相當於是Decorator型別,如果需要具體的功能,可以子類化該型別
/// </summary>
public abstract class DecorationStrategy : House //關鍵點之二,體現關係為Is-a,有這這個關係,裝飾的類也可以繼續裝飾了
{
    //通過組合方式參照Decorator型別,該型別實施具體功能的增加這是關鍵點之一,包含關係,體現為Has-a
    protected House _house;

    protected DecorationStrategy(House house)//通過構造器注入,初始化平臺實現
    {
        this._house = house;
    }

    public override void Renovation()   //該方法就相當於Decorator型別的Operation方法
    {
        if (this._house != null)
        {
            this._house.Renovation();
        }
    }
}

/// <summary>
/// PatrickLiu的房子,我要按我的要求做房子,相當於ConcreteComponent型別,這就是我們具體的餃子餡,我個人比較喜歡韭菜餡
/// </summary>
public sealed class MyHouse : House
{
    public override void Renovation()
    {
        Console.WriteLine("裝修PatrickLiu的房子");
    }
}

/// <summary>
/// 具有安全功能的裝置,可以提供監視和報警功能,相當於ConcreteDecoratorA型別
/// </summary>
public sealed class HouseSecurityDecorator : DecorationStrategy
{
    public HouseSecurityDecorator(House house) : base(house) { }

    public override void Renovation()
    {
        base.Renovation();
        Console.WriteLine("增加安全系統");
    }
}

/// <summary>
/// 具有保溫介面的材料,提供保溫功能,相當於ConcreteDecoratorB型別
/// </summary>
public sealed class KeepWarmDecorator : DecorationStrategy
{
    public KeepWarmDecorator(House house) : base(house) { }

    public override void Renovation()
    {
       base.Renovation();
        Console.WriteLine("增加保溫的功能");
    }
}

public class Program
{
    static void Main()
    {

        House myselfHouse = new MyHouse();//這就是我們的餃子餡,需要裝飾的房子

        DecorationStrategy securityHouse = new HouseSecurityDecorator(myselfHouse);
        securityHouse.Renovation();
        //房子就有了安全系統了

        //如果我既要安全系統又要保暖呢,繼續裝飾就行
        DecorationStrategy securityAndWarmHouse = new HouseSecurityDecorator(securityHouse);
        securityAndWarmHouse.Renovation();
    }
}

六、裝飾模式的實現要點:

  • 通過採用組合、而非繼承的手法,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模式應用的要點在於解決“主體類在多個方向上的擴充套件功能”——是為“裝飾”的含義。

1、裝飾模式的優點:

  • 把抽象介面與其實現解耦。
  • 抽象和實現可以獨立擴充套件,不會影響到對方。
  • 實現細節對客戶透明,對用於隱藏了具體實現細節。

2、裝飾模式的缺點:

  • 增加了系統的複雜度

3、在以下情況下應當使用橋接模式:

  • 如果一個系統需要在構件的抽象化角色和具體化角色之間新增更多的靈活性,避免在兩個層次之間建立靜態的聯絡。
  • 設計要求實現化角色的任何改變不應當影響使用者端,或者實現化角色的改變對使用者端是完全透明的。
  • 需要跨越多個平臺的圖形和視窗系統上。
  • 一個類存在兩個獨立變化的維度,且兩個維度都需要進行擴充套件。

七、.NET 中裝飾模式的實現

在Net框架中,有一個型別很明顯的使用了“裝飾模式”,這個型別就是Stream。Stream型別是一個抽象介面,它在System.IO名稱空間裡面,它其實就是Component。FileStream、NetworkStream、MemoryStream都是實體類ConcreteComponent。右邊的BufferedStream、CryptoStream是裝飾物件,它們都是繼承了Stream介面的。

如圖:

Stream就相當於Component,定義裝飾的物件,FileStream就是要裝飾的物件,BufferedStream是裝飾物件。我們看看BufferedStream的定義,部分定義了。

public sealed class BufferedStream : Stream
 {
    private const int _DefaultBufferSize = 4096; 
    private Stream _stream;
}

到此這篇關於.Net結構型設計模式之裝飾模式(Decorator)的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com