天天看點

設計模式系列-裝飾模式

      通過上篇的簡單描述,我們知道了橋接模式主要是為了解決,一個對象的多個次元的變化因素的變化太快,難以控制的問題,我們通過将每個次元 的變化因素進行抽象, 然後我們的對象隻要依賴于抽象即可,具體的實作調用我們不關心,通過對象組合的方式,我們就能組合出我們想要的對象。無 疑這是一種非常靈活的也是滿足設計模式的原則的,抽象和實作分離,使他們各自發生變化都不受對方的影響。而且我們也講述了,使用橋接模式的幾 個典型的場景,現在我們的實際項目中就有這樣的問題,我也是在項目的使用過程中加深對橋接模式的了解的,橋接模式為系統在多個次元的變化的适 應性方面提供了很好的參考,特别适合底層架構的開發過程中使用,可以适應不同變化因素的改變。上篇橋接模式放出後,我也得到了不少朋友的反 饋,很感謝大家,這裡特别的感謝一個名叫“蕭蕭”的朋友,他指出了我在文章中給出的例子有些不妥之處,我本篇将會對上篇中給出的例子,從給出 符合橋接模式的實作,這裡對大家say sorry !       上篇中的實作:    public interface IORM     {         ISave Save         {             get;             set;         }        IDelete Delete         ICreate Create         ICache Cache         IQuery Query         void Test();    }    我們在這個接口的定義中并沒有展現橋接模式中的變化因素的抽象的概念,我們修改為如下形式: public interface IORM  public interface ICache {     object Cache(object o); } public interface IQuery     object Query(string condition); 具體的緩存服務與查詢服務的實作: public class Cache : ICache          {              public  object Cache(object o){                  return o;                 }         } public class Query: IQuery             public  object Query(string condition){                  return GetList(condition); 具體的ORM實作代碼如下: public class ORM : IORM      {         private IQuery query=new Query();         private ICache cache=new Cache();         public ORM(IQuery query,ICache cache)         {           this.query=query;           this.cache=cache;         }          public ISave Save          {              get              {                  throw new NotImplementedException();              }              set          public IDelete Delete          public ICreate Create          public void Test()              ///測試緩存對象!              this.cache.Cache(new object());      }       對于之前按照我給的思路的方式,請大家糾正下思路,我這裡對大家造成的誤解,表示抱歉,因為我舉例子的時候,表述的和我們本文的初衷有些 偏差,希望大家見諒,請大家繼續提出寶貴意見和建議。

        本篇将會開始講述結構型模式中的很有技巧性的模式之一-裝飾模式,我們了解的裝飾就是給類穿個衣服,或者裝扮一下,讓某個類具有一個新的特性或者新的

職責。我們在應對對于通過類的繼承來實作類擴充功能時候的膨脹的速度過快的問題,我想這個也是我們上一篇講述的橋接模式中提到的一些問題,不過裝飾模式和

橋接模式的側重點稍微不同。裝飾模式更側重的是某個類型的功能經常的動态的增加的情況。那麼如果我們通過繼承的方式來實作,那麼将會是噩夢。繼承複繼承,

繼承何其多,然而通過裝飾模式,我們就能很優雅的為某個類型添加新的職責。我們也知道,由于C#不支援多繼承,我們隻能為某個類型添加新增功能的時候,我們

隻能單繼承+多實作接口的形式來為類型添加新的功能。而裝飾模式可以很優雅的解決這樣的問題。

         裝飾模式的主要目的就是:動态的為某個類型添加新的職責。

          我們來看看裝飾模式的過程吧:

           我們将自己開發一個簡單的播放器,我們通過第一次裝飾可以為播放器增加播放音樂的功能,具體包括播放所有的音頻格式檔案,通過第二次裝飾,我們可

以為這個播放器添加播放視訊格式檔案的功能,這時候,如果我們想要一個萬能的播放器,那麼我們将通過2次裝飾來完成這個播放器的類型職責的添加。

       a、上篇回顧。        b、摘要。        c、本文大綱。        d、裝飾模式的特點及使用場景。        e、裝飾模式的經典實作。        f、裝飾模式的其他方案。        g、裝飾模式使用總結。        h、系列進度。        i、下篇預告。
        裝飾模式:裝飾模式主要是解決了,我們以動态的,透明的形式為某個類型添加一個新的職責,客戶程式可以不知道我們具體添加的功能職責, 而客戶程式隻是根據對象提供的方法進行調用即可。而具體職責操作留給裝飾對象去完成。         我們這裡給出裝飾模式的經典模式的類圖:         我們這裡對這個經典結構進行稍微的說明:我們這裡結合我們上面的播放器來說         1、對于Compnent,我們定義一個IPlayer這樣的接口,我們接口中定義方法,Play()方法。         2、然後我們把要裝飾這個播放器的裝飾類型進行抽象,抽象出接口IDecorator接口。這個即可繼承自IPlayer接口。         3、接着,我們定義一個簡單的播放器對象,實作這個IPlayer接口。         4、我們實作播放音樂和視訊檔案的裝飾類型的實作。         5、我們将簡單的播放器對象,通過裝飾對象進行裝飾,為這個簡單點傳播放器套上一層一層的外殼,使其具有播放音樂和視訊的功能。
         1、當我們需要為某個現有的對象,動态的增加一個新的功能或職責時,可以考慮使用裝飾模式。          2、适應于某個對象的職責經常發生變化或者經常需要動态的增加職責,避免因為這種為了适應這樣的變化,而增加繼承子類擴充的方式,因為 這種方式為 造成,子類膨脹的速度過快,難以控制。

        經過上面的簡單描述,我想大家很想知道裝飾模式的經典實作方案吧!我們來結合播放器的執行個體來分析實作,下面給出核心代碼:

   /// <summary>     /// 簡單點傳播放器的統一接口     /// </summary>     public interface IPlayer         void Play(); 
    /// <summary>      /// 播放器的普通實作類      /// </summary>      public class Player : IPlayer      {          #region IPlayer 成員         public void Play()              throw new NotImplementedException();          #endregion     /// 裝飾器接口      public  interface IDecorator : IPlayer     {     /// 裝飾器的抽象基類      public abstract class DecoratorBase : IDecorator          protected IPlayer play = null;          public DecoratorBase(IPlayer player)              this.play = player;          public abstract void Play();     /// 音樂播放器裝飾器       public class MusicDecorator : DecoratorBase           public MusicDecorator(IPlayer play)              : base(play)          public override void Play()      /// 視訊播放器裝飾類      public class VedioDecorator : DecoratorBase          public VedioDecorator(IPlayer play)     具體的測試調用代碼如下:         static void Main(string[] args)              IPlayer play = new Player();              play = new MusicDecorator(play);              play = new VedioDecorator(play);  經過了2次的裝飾後,該播放器對象,可以播放任何格式的檔案。通過上面的過程,我們發現了我們在使用裝飾模式的時候,有如下幾點我們需要把我好: 1、我們在開發的過程中,如果需要為某個類型添加多個職責的時候,我們需要多個裝飾實作類,這樣類太多的時候,我們需要将這個對象裝飾多次。 2、我們對我們的裝飾過的對象,如何指定的撤銷某個裝飾過程的狀态呢?類似我們使用某些應用程式中的undo操作。 3、對于動态裝飾的對象,我們debug的時候,我們很難跟蹤和調試。

             裝飾模式的具體過程如下:

       一、通過配置檔案來完成裝飾操作
       具體的操作過程如下:配置檔案中配置對象要完成的裝飾步驟,通過公共的配置讀取類,通過字典緩存裝飾的步驟,然後傳入普通的對象,傳回裝 飾後的對象。 <a href="http://images.cnblogs.com/cnblogs_com/hegezhou_hot/201012/201012112040388777.png"></a>        我們這裡給出配置檔案的格式: &lt;?xml version="1.0" encoding="utf-8" ?&gt;  &lt;Decorator&gt;    &lt;Section&gt;      &lt;Class  name="" type=""&gt;        &lt;Step name="" type=""/&gt;      &lt;/Class&gt;    &lt;/Section&gt;  &lt;/Decorator&gt; 我們來看具體的裝飾器步驟構造器代碼:     public class DecoratorFactory          public static T BuilderPlay&lt;T&gt;(T play)              Dictionary&lt;Type, List&lt;IDecorator&gt;&gt; steps = new Dictionary&lt;Type, List&lt;IDecorator&gt;&gt;();              List&lt;IDecorator&gt; list = steps[typeof(T)];             foreach (IDecorator item in list)                  play = (T)Activator.CreateInstance(item.GetType(), play);              }             return play;         二、通過特性+反射的形式來組織裝飾的過程        這裡就不給出具體的實作代碼了,下篇或者後續給出分析幾類基于特性+标記的實作方案。大夥也可以提出其他的不同實作思路。

        通過上面的講述,我們知道裝飾模式,特别适合對某個類型的對象,動态的增加新的職責,應用程式就像使用原來的對象一樣使用對象新增的裝飾後的功能,裝

飾模式就好像是穿了一層層的外殼,這樣的方式避免了通過繼承來為類型添加新的職責的形式可取,通過繼承的方式容易造成子類的膨脹,但是當裝飾類太多的時

候,也是個難以維護的問題,至少是在裝飾對象的時候,我們可能需要多步操作來完成對象的裝飾,這時候我們可以同上面提出的改進的方案,來完成自動配置裝飾

模式,記錄操作模式的狀态,可以進行有效的復原操作,以完成撤銷操作。

本文轉自 hot的fans  51CTO部落格,原文連結:http://blog.51cto.com/2435232/560921