【23種設計模式】裝飾模式(九)

2023-09-12 12:00:58

前言

裝飾模式,英文名稱:Decorator Pattern。我第一次看到這個名稱想到的是另外一個詞語「裝修」,我就說說我對「裝修」的理解吧,大家一定要看清楚,是「裝修」,不是「裝飾」。在房子裝修的過程中,各種功能可以相互組合,來增加房子的功用。類似的,如果我們在軟體系統中,要給某個型別或者物件增加功能,如果使用「繼承」的方案來寫程式碼,就會出現子類暴漲的情況。比如:IMarbleStyle是大理石風格的一個功能,IKeepWarm是保溫的一個介面定義,IHouseSecurity是房子安全的一個介面,就三個介面來說,House是我們房子,我們的房子要什麼功能就實現什麼介面,如果房子要的是複合功能,介面不同的組合就有不同的結果,這樣就導致我們子類膨脹嚴重,如果需要在增加功能,子類會成指數增長。

裝飾模式的定義

上述的問題的根源在於我們「過度地使用了繼承來擴充套件物件的功能」,由於繼承為型別引入的靜態特質,所謂靜態特質,就是說如果想要某種功能,我們必須在編譯的時候就要定義這個類,這也是強型別語言的特點。靜態,就是指在編譯的時候要確定的東西;動態,是指執行時確定的東西。使得這種擴充套件方式缺乏靈活性;並且隨著子類的增多(擴充套件功能的增多),各種子類的組合(擴充套件功能的組合)會導致更多子類的膨脹(多繼承)。如何使「物件功能的擴充套件」能夠根據需要來動態(即執行時)地實現?同時避免「擴充套件功能的增多」帶來的子類膨脹問題?從而使得任何「功能擴充套件變化」所導致的影響降為最低?裝飾者模式解決此問題應運而生,動態地給一個物件增加一些額外的職責。

裝飾模式的組成

  • 抽象構件角色(Component):給出一個抽象介面,以規範準備接收附加責任的物件。

  • 具體構件角色(Concrete Component):定義一個將要接收附加責任的類。

  • 裝飾角色(Decorator):持有一個構件(Component)物件的範例,並實現一個與抽象構件介面一致的介面。

  • 具體裝飾角色(Concrete Decorator):負責給構件物件新增上附加的責任。

裝飾模式的實現

以裝修房子為例,完成裝飾著模式的程式碼實現

房子定義
    /// <summary>
    /// 該抽象類就是房子抽象介面的定義,該型別就相當於是Component型別,是餃子餡,需要裝飾的,需要包裝的
    /// </summary>
    public abstract class House
    {
        /// <summary>
        /// 房子的裝修方法--該操作相當於Component型別的Operation方法
        /// </summary>
        public abstract void Renovation();
    }
   /// <summary>
    /// MyHouse的房子,我要按我的要求做房子,相當於ConcreteComponent型別
    /// </summary>
    public sealed class MyHouse : House
    {
        public override void Renovation()
        {
            Console.WriteLine("裝修我的房子");
        }
    }
裝飾類的定義
 /// <summary>
    /// 該抽象類就是裝飾介面的定義,該型別就相當於是Decorator型別,如果需要具體的功能,可以子類化該型別
    /// </summary>
    public abstract class DecorationStrategy : House //關鍵點之二,體現關係為Is-a,有了這個關係,裝飾的類也可以繼續裝飾了
    {
        //通過組合方式參照Decorator型別,該型別實施具體功能的增加
        //這是關鍵點之一,包含關係,體現為Has-a
        protected House _house;

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

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

安全需求類裝飾定義
/// <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 void RunTest()
        {
            //這就是我們需要裝飾的房子
            House myselfHouse = new MyHouse();

            DecorationStrategy securityHouse = new HouseSecurityDecorator(myselfHouse);
            securityHouse.Renovation();

            /*
             * 此時房子就有了安全系統了.....
             */

            //【1】如果我既要安全系統又要保暖呢,繼續裝飾就行
            //DecorationStrategy securityAndWarmHouse = new KeepWarmDecorator(myselfHouse);
            //securityAndWarmHouse.Renovation();


            Console.WriteLine("\r\n*****************************\r\n");

            //【2】如果我既要安全系統又要保暖呢,繼續裝飾就行【和上邊的進行執行比對】
            //【對執行結果難理解的話,打斷點單步執行進行理解】

            DecorationStrategy securityAndWarmHouse1 = new KeepWarmDecorator(securityHouse);
            securityAndWarmHouse1.Renovation();
        }

裝飾模式的優缺點

優點
  • 把抽象介面與其實現解耦。

  • 抽象和實現可以獨立擴充套件,不會影響到對方。

  • 實現細節對客戶透明,對使用者隱藏了具體實現細節。

缺點
  • 增加了系統的複雜度