c#裝飾器模式詳解

来源:https://www.cnblogs.com/mingnianjiehunba/archive/2023/11/09/17732216.html
-Advertisement-
Play Games

從接觸領域驅動設計的初學階段,到實現一個舊系統改造到DDD模型,再到按DDD規範落地的3個的項目。對於領域驅動模型設計研發,從開始的各種疑惑到吸收各種先進的理念,目前在技術實施這一塊已經基本比較成熟。在既往經驗中總結了一些在開發中遇到的技術問題和解決方案進行分享。 ...


基礎介紹:

 動態地給一個對象添加一些額外的職責。適用於需要擴展一個類的功能,或給一個類添加多個變化的情況。

  裝飾器,顧名思義就是在原有基礎上添加一些功能。

  大家都只知道如果想單純的給原有類增加一些功能,可以直接繼續該類生成一個子類就可以。

  舉個例子,如果現在有個手機類,想給手機貼膜,傳統的做法就是新建一個手機類的子類(手機貼膜子類),繼承自手機類。

  使用這個子類就可以完成對手機的貼膜操作。

  那如果又想給手機按保護殼的話,傳統做法有兩種,可以繼續新建一個手機類的子類(手機保護殼子類),繼承自手機類。

  使用這個子類可以給手機按保護殼,但也就失去了給手機貼膜的功能。另一種做法,新建一個手機貼膜類的子類(手機貼膜+保護殼),也就是手機類的子子類。

  這樣即可以貼膜也可以按手機殼。

  大家思考一個問題,如果有很多個裝飾並且想隨意組合的話,那就有N個子類並且存在很深的繼承鏈路。

  想要解決這個問題,就可以用到裝飾器了。

  比如貼膜裝飾、保護殼裝飾、貼紙裝飾等等,它們都是獨立存在的,只繼承自裝飾器類。

  什麼意思呢?就是說給手機貼膜的時候它並不會給手機按保護殼的功能,職責單一,貼膜裝飾器只負責給手機貼膜。

  這樣做有什麼好處呢?好處就是這些裝飾可以隨意組合,比如即想貼膜又想按保護殼,就可以將貼膜裝飾+保護殼裝飾進組組合。

  

  在裝飾器模式中各個角色有:

  • 抽象構件(Component)角色:規範手機的構成
  • 具體構件(ConcreteComponent)角色:繼承自抽象構件,具體實現手機。(大多情況下,可以省略抽象構件,抽象裝飾類可以直接繼承)
  • 抽象裝飾類(Decorator)角色:創建一個構件(Component)對象的實例,可以使用這個實例調用原構件的功能,並規範裝飾類。
  • 具體裝飾類(ConcreteDecorator)角色:繼承自抽象裝飾類,具體實現該裝飾功能。

應用場景:

 原有類無法修改或者修改困難的情況下,對類進行多次擴展或功能性比較相互獨立,有效防止多次擴展的情況下子類的膨脹。

  註:如果類的擴展比較簡單,並且不會多次進行擴展的情況下直接使用類的繼承生成子類的方式更為方便快捷。

創建方式:

 為了方便說明,以下實例就不創建抽象構件了。

  1. 首先先看下不使用裝飾器進行類的擴展

     1     /// <summary>
     2     /// 手機類
     3     /// </summary>
     4     public class Phone
     5     {
     6         public void Print()
     7         {
     8             Console.WriteLine("手機");
     9         }
    10     }
    11 
    12     /// <summary>
    13     /// 手機貼膜
    14     /// </summary>
    15     public class Sticker : Phone
    16     {
    17         public Sticker()
    18         {
    19             base.Print();
    20         }
    21 
    22         /// <summary>
    23         /// 進行貼膜
    24         /// </summary>
    25         public void AddSticker()
    26         {
    27             Console.WriteLine("給手機貼膜");
    28         }
    29     }
    30 
    31     /// <summary>
    32     /// 手機保護殼
    33     /// </summary>
    34     public class ProtectiveCase : Phone
    35     {
    36         public ProtectiveCase()
    37         {
    38             base.Print();
    39         }
    40 
    41         /// <summary>
    42         /// 按保護殼
    43         /// </summary>
    44         public void AddProtectiveCase()
    45         {
    46             Console.WriteLine("給手機按保護殼");
    47         }
    48     }
    49 
    50     /// <summary>
    51     /// 即貼膜又按保護殼
    52     /// </summary>
    53     public class ProtectiveCaseAndSticker : Sticker
    54     {
    55         public ProtectiveCaseAndSticker()
    56         {
    57         }
    58 
    59         /// <summary>
    60         /// 按保護殼
    61         /// </summary>
    62         public void AddProtectiveCase()
    63         {
    64             Console.WriteLine("給手機按保護殼");
    65         }
    66     }
    67 
    68     /// <summary>
    69     /// 客戶端
    70     /// </summary>
    71     class Client
    72     {
    73         static void Main(string[] args)
    74         {
    75             //創建一個手機
    76             Phone phone = new Phone();
    77             phone.Print();
    78             Console.WriteLine("\r\n");
    79 
    80             //給手機貼膜
    81             Sticker sticker = new Sticker();
    82             sticker.AddSticker();
    83             Console.WriteLine("\r\n");
    84 
    85             //給手機按保護殼
    86             ProtectiveCase protectiveCase = new ProtectiveCase();
    87             protectiveCase.AddProtectiveCase();
    88             Console.WriteLine("\r\n");
    89 
    90             //即貼膜又按保護殼
    91             ProtectiveCaseAndSticker protectiveCaseAndSticker = new ProtectiveCaseAndSticker();
    92             protectiveCaseAndSticker.AddSticker();
    93             protectiveCaseAndSticker.AddProtectiveCase();
    94             Console.ReadKey();
    95         }
    96     }

    通過上述實例可以看出,如果各個擴展功能比較獨立的話可以直接進行繼承擴展。

    如果各個功能直接有交集的情況下,會造成很深的繼承關係。

    比如上述實例中,如果單獨貼膜或者單獨安裝保護殼則直接繼承手機類即可。

    但如果想要即貼膜又要安裝保護殼,各自繼承手機類的方式就行不通了,只能在貼膜類或者保護殼類的基礎上進行擴展。

    如果還有添加手機掛飾,那就還需要再一層繼承關係。

    要解決這個問題就用到了裝飾器,下麵看看使用裝飾器是怎麼給手機添加新功能的。

  2. 使用裝飾器模式對類進行擴展

     1     /// <summary>
     2     /// 手機類
     3     /// </summary>
     4     public class Phone
     5     {
     6         public void Print()
     7         {
     8             Console.WriteLine("手機");
     9         }
    10     }
    11 
    12     /// <summary>
    13     /// 裝飾抽象類
    14     /// </summary>
    15     public abstract class Decorator : Phone
    16     {
    17         private Phone phone;
    18 
    19         public Decorator(Phone p)
    20         {
    21             this.phone = p;
    22         }
    23 
    24         public abstract void AddDecorator();
    25     }
    26 
    27     /// <summary>
    28     /// 貼膜裝飾
    29     /// </summary>
    30     public class Sticker : Decorator
    31     {
    32         public Sticker(Phone p)
    33               : base(p)
    34         {
    35         }
    36 
    37         public override void AddDecorator()
    38         {
    39             Console.WriteLine("給手機貼膜");
    40         }
    41     }
    42 
    43     /// <summary>
    44     /// 保護殼裝飾
    45     /// </summary>
    46     public class ProtectiveCase : Decorator
    47     {
    48         public ProtectiveCase(Phone p)
    49              : base(p)
    50         {
    51         }
    52 
    53         /// <summary>
    54         /// 按保護殼
    55         /// </summary>
    56         public override void AddDecorator()
    57         {
    58             Console.WriteLine("給手機按保護殼");
    59         }
    60     }
    61 
    62     /// <summary>
    63     /// 客戶端
    64     /// </summary>
    65     class Client
    66     {
    67         static void Main(string[] args)
    68         {
    69             //單獨給手機貼膜
    70             Phone phone = new Phone();
    71             phone.Print();
    72             Decorator sticker = new Sticker(phone);
    73             sticker.AddDecorator();
    74 
    75             //單獨給手機按保護殼
    76             phone = new Phone();
    77             phone.Print();
    78             Decorator protectiveCase = new ProtectiveCase(phone);
    79             protectiveCase.AddDecorator();
    80             Console.WriteLine("\r\n");
    81 
    82             //即貼膜又按保護殼
    83             phone = new Phone();
    84             phone.Print();
    85             //首先創建貼膜裝飾實例,將手機對象傳入
    86             Decorator decorator = new Sticker(phone);
    87             //進行貼膜操作
    88             decorator.AddDecorator();
    89             //創建保護殼裝飾實例,將貼膜後的手機對象傳入
    90             decorator = new ProtectiveCase(decorator);
    91             //進行按保護殼操作
    92             decorator.AddDecorator();
    93             Console.ReadKey();
    94         }
    95     }

    從上述實例中可以看出,各個裝飾類只對裝飾抽象類負責,職責單一。

    各個裝飾進行組合時,方便隨意。新增裝飾時,只需要新增一個繼承自裝飾抽象類的子類即可實現以原有裝飾的隨意組合使用。

總結:

  想要擴展一個類的時候,傳統的繼承生成子類的形式,適用於擴展簡單,並且不會多次擴展的情況下。

  而如果一個類的擴展是周期性,多次擴展的或功能性比較相互獨立的情況下,可以使用裝飾器,可以有效的解決傳統繼承擴展子類膨脹的問題。

  裝飾模式是繼承的一個替代模式,裝飾模式可以動態擴展一個實現類的功能。

  

  

作者:少年真愛 出處:https://www.cnblogs.com/mingnianjiehunba/p/17732216.html 博主的文章沒有高度、深度和廣度,只是湊字數。由於博主的水平不高,不足和錯誤之處在所難免,希望大家能夠批評指出。 博主是利用讀書、參考、引用、抄襲、複製和粘貼等多種方式打造成自己的文章,請原諒博主成為一個無恥的文檔搬運工!
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文分享自華為雲社區《Proxy下的Prepare透傳,讓GaussDB(for MySQL)更穩固,性能更卓越》,作者: GaussDB 資料庫 。 1.引言 在很多業務場景下,資料庫應用程式處理大量相同的SQL語句——只需更改SQL語句中的文字或變數值。例如:使用相同的SQL模板進行WHERE查 ...
  • 本文目標是:瞭解查詢的核心原理,對比 SQL 查詢優化技巧在 h2database 中的落地實現。前提:為了貼近實際應用,本文 Code Insight 基於 BTree 存儲引擎。 ...
  • 在構建數據倉庫或做數據分析時,需要對原始數據的結構進行一定的處理,有時涉及到“行轉列”,有時涉及到“列轉行”,那麼這兩個轉換的方式具體是什麼,有什麼差異,怎麼實現。 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 在寫頁面的時候,發現表單裡面有一個省市區的 options 組件要寫,因為表單很多地方都會用到這個地址選擇,我便以為很簡單嘛。 雖然很簡單的一個功能,但是網路上能搜索到的教程大多都是需要配合 elementUI 等各種 UI 庫的,但是我 ...
  • 我們有沒有想過,是否有一種技術,伺服器可以主動將數據推送給客戶端進行渲染,而不再是客戶端向伺服器發出請求等待返回結果呢?接下來,讓我們一起瞭解weboskcet ...
  • 前言 最近新開了個項目,以前老項目都是vue2+vuex開發的,都說用vue3+pinia爽得多,那新項目就vue3+pinia吧。這裡記錄一下pinia的使用。 使用方法 安裝pinia: npm i pinia main.js中引入pinia: //main.js import { create ...
  • 作者:WangMin 格言:努力做好自己喜歡的每一件事 jQuery.js 是什麼? jQuery是一個快速簡潔、免費開源易用的JavaScript框架,倡導寫更少的代碼,做更多的事情 。它封裝JavaScript常用的功能代碼,提供了一種簡便的JavaScript設計模式,以及我們開發中常用到的操 ...
  • 在日常一些需求中,總會遇到一些需要前端進行手動計算的場景,那麼這裡需要優先考慮的則是數字精度問題!具體請看下麵截圖 如圖所示,在JavaScript進行浮點型數據計算當中,會出現計算結果“不正確”的現象。 我們知道浮點型數據類型主要有:單精度float、雙精度double。 浮點型簡單來說就是表示帶 ...
一周排行
    -Advertisement-
    Play Games
  • 下麵是一個標準的IDistributedCache用例: public class SomeService(IDistributedCache cache) { public async Task<SomeInformation> GetSomeInformationAsync (string na ...
  • 這個庫提供了在啟動期間實例化已註冊的單例,而不是在首次使用它時實例化。 單例通常在首次使用時創建,這可能會導致響應傳入請求的延遲高於平時。在註冊時創建實例有助於防止第一次Request請求的SLA 以往我們要在註冊的時候實例單例可能會這樣寫: //註冊: services.AddSingleton< ...
  • 最近公司的很多項目都要改單點登錄了,不過大部分都還沒敲定,目前立刻要做的就只有一個比較老的項目 先改一個試試手,主要目標就是最短最快實現功能 首先因為要保留原登錄方式,所以頁面上的改動就是在原來登錄頁面下加一個SSO登錄入口 用超鏈接寫的入口,頁面改造後如下圖: 其中超鏈接的 href="Staff ...
  • Like運算符很好用,特別是它所提供的其中*、?這兩種通配符,在Windows文件系統和各類項目中運用非常廣泛。 但Like運算符僅在VB中支持,在C#中,如何實現呢? 以下是關於LikeString的四種實現方式,其中第四種為Regex正則表達式實現,且在.NET Standard 2.0及以上平... ...
  • 一:背景 1. 講故事 前些天有位朋友找到我,說他們的程式記憶體會偶發性暴漲,自己分析了下是非托管記憶體問題,讓我幫忙看下怎麼回事?哈哈,看到這個dump我還是非常有興趣的,居然還有這種游戲幣自助機類型的程式,下次去大玩家看看他們出幣的機器後端是不是C#寫的?由於dump是linux上的程式,剛好win ...
  • 前言 大家好,我是老馬。很高興遇到你。 我們為 java 開發者實現了 java 版本的 nginx https://github.com/houbb/nginx4j 如果你想知道 servlet 如何處理的,可以參考我的另一個項目: 手寫從零實現簡易版 tomcat minicat 手寫 ngin ...
  • 上一次的介紹,主要圍繞如何統一去捕獲異常,以及為每一種異常添加自己的Mapper實現,並且我們知道,當在ExceptionMapper中返回非200的Response,不支持application/json的響應類型,而是寫死的text/plain類型。 Filter為二方包異常手動捕獲 參考:ht ...
  • 大家好,我是R哥。 今天分享一個爽飛了的面試輔導 case: 這個杭州兄弟空窗期 1 個月+,面試了 6 家公司 0 Offer,不知道問題出在哪,難道是杭州的 IT 崩盤了麽? 報名面試輔導後,經過一個多月的輔導打磨,現在成功入職某上市公司,漲薪 30%+,955 工作制,不咋加班,還不捲。 其他 ...
  • 引入依賴 <!--Freemarker wls--> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.30</version> </dependency> ...
  • 你應如何運行程式 互動式命令模式 開始一個互動式會話 一般是在操作系統命令行下輸入python,且不帶任何參數 系統路徑 如果沒有設置系統的PATH環境變數來包括Python的安裝路徑,可能需要機器上Python可執行文件的完整路徑來代替python 運行的位置:代碼位置 不要輸入的內容:提示符和註 ...