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
  • 1、預覽地址:http://139.155.137.144:9012 2、qq群:801913255 一、前言 隨著網路的發展,企業對於信息系統數據的保密工作愈發重視,不同身份、角色對於數據的訪問許可權都應該大相徑庭。 列如 1、不同登錄人員對一個數據列表的可見度是不一樣的,如數據列、數據行、數據按鈕 ...
  • 前言 上一篇文章寫瞭如何使用RabbitMQ做個簡單的發送郵件項目,然後評論也是比較多,也是準備去學習一下如何確保RabbitMQ的消息可靠性,但是由於時間原因,先來說說設計模式中的簡單工廠模式吧! 在瞭解簡單工廠模式之前,我們要知道C#是一款面向對象的高級程式語言。它有3大特性,封裝、繼承、多態。 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 介紹 Nodify是一個WPF基於節點的編輯器控制項,其中包含一系列節點、連接和連接器組件,旨在簡化構建基於節點的工具的過程 ...
  • 創建一個webapi項目做測試使用。 創建新控制器,搭建一個基礎框架,包括獲取當天日期、wiki的請求地址等 創建一個Http請求幫助類以及方法,用於獲取指定URL的信息 使用http請求訪問指定url,先運行一下,看看返回的內容。內容如圖右邊所示,實際上是一個Json數據。我們主要解析 大事記 部 ...
  • 最近在不少自媒體上看到有關.NET與C#的資訊與評價,感覺大家對.NET與C#還是不太瞭解,尤其是對2016年6月發佈的跨平臺.NET Core 1.0,更是知之甚少。在考慮一番之後,還是決定寫點東西總結一下,也回顧一下.NET的發展歷史。 首先,你沒看錯,.NET是跨平臺的,可以在Windows、 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 添加節點(nodes) 通過上一篇我們已經創建好了編輯器實例現在我們為編輯器添加一個節點 添加model和viewmode ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...
  • 類型檢查和轉換:當你需要檢查對象是否為特定類型,並且希望在同一時間內將其轉換為那個類型時,模式匹配提供了一種更簡潔的方式來完成這一任務,避免了使用傳統的as和is操作符後還需要進行額外的null檢查。 複雜條件邏輯:在處理複雜的條件邏輯時,特別是涉及到多個條件和類型的情況下,使用模式匹配可以使代碼更 ...
  • 在日常開發中,我們經常需要和文件打交道,特別是桌面開發,有時候就會需要載入大批量的文件,而且可能還會存在部分文件缺失的情況,那麼如何才能快速的判斷文件是否存在呢?如果處理不當的,且文件數量比較多的時候,可能會造成卡頓等情況,進而影響程式的使用體驗。今天就以一個簡單的小例子,簡述兩種不同的判斷文件是否... ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...