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 微服務框架,幫助我們輕鬆構建和管理微服務應用。 本框架不僅支持 Consul 服務註 ...
  • 先看一下效果吧: 如果不會寫動畫或者懶得寫動畫,就直接交給Blend來做吧; 其實Blend操作起來很簡單,有點類似於在操作PS,我們只需要設置關鍵幀,滑鼠點來點去就可以了,Blend會自動幫我們生成我們想要的動畫效果. 第一步:要創建一個空的WPF項目 第二步:右鍵我們的項目,在最下方有一個,在B ...
  • Prism:框架介紹與安裝 什麼是Prism? Prism是一個用於在 WPF、Xamarin Form、Uno 平臺和 WinUI 中構建鬆散耦合、可維護和可測試的 XAML 應用程式框架 Github https://github.com/PrismLibrary/Prism NuGet htt ...
  • 在WPF中,屏幕上的所有內容,都是通過畫筆(Brush)畫上去的。如按鈕的背景色,邊框,文本框的前景和形狀填充。藉助畫筆,可以繪製頁面上的所有UI對象。不同畫筆具有不同類型的輸出( 如:某些畫筆使用純色繪製區域,其他畫筆使用漸變、圖案、圖像或繪圖)。 ...
  • 前言 嗨,大家好!推薦一個基於 .NET 8 的高併發微服務電商系統,涵蓋了商品、訂單、會員、服務、財務等50多種實用功能。 項目不僅使用了 .NET 8 的最新特性,還集成了AutoFac、DotLiquid、HangFire、Nlog、Jwt、LayUIAdmin、SqlSugar、MySQL、 ...
  • 本文主要介紹攝像頭(相機)如何採集數據,用於類似攝像頭本地顯示軟體,以及流媒體數據傳輸場景如傳屏、視訊會議等。 攝像頭採集有多種方案,如AForge.NET、WPFMediaKit、OpenCvSharp、EmguCv、DirectShow.NET、MediaCaptre(UWP),網上一些文章以及 ...
  • 前言 Seal-Report 是一款.NET 開源報表工具,擁有 1.4K Star。它提供了一個完整的框架,使用 C# 編寫,最新的版本採用的是 .NET 8.0 。 它能夠高效地從各種資料庫或 NoSQL 數據源生成日常報表,並支持執行複雜的報表任務。 其簡單易用的安裝過程和直觀的設計界面,我們 ...
  • 背景需求: 系統需要對接到XXX官方的API,但因此官方對接以及管理都十分嚴格。而本人部門的系統中包含諸多子系統,系統間為了穩定,程式間多數固定Token+特殊驗證進行調用,且後期還要提供給其他兄弟部門系統共同調用。 原則上:每套系統都必須單獨接入到官方,但官方的接入複雜,還要官方指定機構認證的證書 ...
  • 本文介紹下電腦設備關機的情況下如何通過網路喚醒設備,之前電源S狀態 電腦Power電源狀態- 唐宋元明清2188 - 博客園 (cnblogs.com) 有介紹過遠程喚醒設備,後面這倆天瞭解多了點所以單獨加個隨筆 設備關機的情況下,使用網路喚醒的前提條件: 1. 被喚醒設備需要支持這WakeOnL ...
  • 前言 大家好,推薦一個.NET 8.0 為核心,結合前端 Vue 框架,實現了前後端完全分離的設計理念。它不僅提供了強大的基礎功能支持,如許可權管理、代碼生成器等,還通過採用主流技術和最佳實踐,顯著降低了開發難度,加快了項目交付速度。 如果你需要一個高效的開發解決方案,本框架能幫助大家輕鬆應對挑戰,實 ...