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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...