深入理解設計模式(19):裝飾模式

来源:https://www.cnblogs.com/xuwendong/archive/2019/03/07/10180943.html
-Advertisement-
Play Games

一、前言 裝飾模式實際上是一直提倡的組合代替繼承的實踐方式,個人認為要理解裝飾者模式首先需要理解為什麼需要組合代替繼承,繼承又是為什麼讓人深惡痛絕. 為什麼建議使用組合代替繼承? 面向對象的特性有繼承與封裝,但兩者卻又有一點矛盾,繼承意味子類依賴了父類中的實現,一旦父類中改變實現則會對子類造成影響, ...


一、前言

裝飾模式實際上是一直提倡的組合代替繼承的實踐方式,個人認為要理解裝飾者模式首先需要理解為什麼需要組合代替繼承,繼承又是為什麼讓人深惡痛絕.

為什麼建議使用組合代替繼承?

面向對象的特性有繼承與封裝,但兩者卻又有一點矛盾,繼承意味子類依賴了父類中的實現,一旦父類中改變實現則會對子類造成影響,這是打破了封裝性的一種表現. 而組合就是巧用封裝性來實現繼承功能的代碼復用.

二、什麼是裝飾模式

1.定義:

裝飾器模式又名包裝(Wrapper)模式。裝飾器模式以對客戶端透明的方式拓展對象的功能,是繼承關係的一種替代方案。

2.意圖

動態地給一個對象添加一些額外的職責。就增加功能來說,Decorator模式相比生成子類更為靈活。

3.別名

包裝器Wrapper

4.動機

有時我們希望給某個對象而不是整個類添加一些功能。例如,一個圖形用戶界面工具箱允許你對任意一個用戶界面組件添加一些組件,例如邊框,或是一些行為,例如視窗滾動等。

5.作用

在不修改原有的介面的情況下,讓類表現的更好。

6.問題

自然是繼承有一些問題
繼承會導致超類和子類之間存在強耦合性,當超類改變時,子類也會隨之改變; 
超類的內部細節對於子類是可見的,繼承常常被認為破壞了封裝性; 

三、裝飾模式的結構

 

在裝飾器模式中的角色有:

  • 抽象構件(Component)角色:給出一個抽象介面,已規範準備接收附加責任的對象。
  • 具體構件(ConcreteComponent)角色:定義一個將要接收附加責任的類
  • 裝飾(Decorator)角色:持有一個構件(Component)對象的實例,並定義一個與抽象構件介面一致的介面。
  • 具體裝飾(ConcreteDecorator)角色:負責給構件對象“貼上”附加的責任。

四、裝飾模式的使用場景

  1. 需要擴展一個類的功能或給一個類增加附加責任。

  2. 需要動態地給一個對象增加功能,這些功能可以再動態地撤銷。

  3. 需要增加由一些基本功能的排列組合而產生的非常大量的功能

五、裝飾模式的優缺點

優點:

  1. 裝飾這模式和繼承的目的都是擴展對象的功能,但裝飾者模式比繼承更靈活

  2. 通過使用不同的具體裝飾類以及這些類的排列組合,設計師可以創造出很多不同行為的組合

  3. 裝飾者模式有很好地可擴展性

缺點

  裝飾者模式會導致設計中出現許多小對象,如果過度使用,會讓程式變的更複雜。並且更多的對象會是的差錯變得困難,特別是這些對象看上去都很像。

六、裝飾模式的實現

/// <summary>
    /// 手機抽象類,即裝飾者模式中的抽象組件類
    /// </summary>
    public abstract class Phone
    {
        public abstract void Print();
    }

    /// <summary>
    /// 蘋果手機,即裝飾著模式中的具體組件類
    /// </summary>
    public class ApplePhone:Phone
    {
        /// <summary>
        /// 重寫基類方法
        /// </summary>
        public override void Print()
        {
            Console.WriteLine("開始執行具體的對象——蘋果手機");
        }
    }

    /// <summary>
    /// 裝飾抽象類,要讓裝飾完全取代抽象組件,所以必須繼承自Photo
    /// </summary>
    public abstract class Decorator:Phone
    {
        private Phone phone;

        public Decorator(Phone p)
        {
            this.phone = p;
        }

        public override void Print()
        {
            if (phone != null)
            {
                phone.Print();
            }
        }
    }

    /// <summary>
    /// 貼膜,即具體裝飾者
    /// </summary>
    public class Sticker : Decorator
    {
        public Sticker(Phone p)
            : base(p)
        { 
        }

        public override void Print()
        {
            base.Print();

            // 添加新的行為
            AddSticker();      
        }

        /// <summary>
        /// 新的行為方法
        /// </summary>
        public void AddSticker()
        {
            Console.WriteLine("現在蘋果手機有貼膜了");
        }
    }

    /// <summary>
    /// 手機掛件
    /// </summary>
    public class Accessories : Decorator
    {
        public Accessories(Phone p)
            : base(p)
        {
        }

        public override void Print()
        {
            base.Print();

            // 添加新的行為
            AddAccessories();          
        }

        /// <summary>
        /// 新的行為方法
        /// </summary>
        public void AddAccessories()
        {
            Console.WriteLine("現在蘋果手機有漂亮的掛件了");
        }
    }

客戶端代碼

class Customer
    {
        static void Main(string[] args)
        {
            // 我買了個蘋果手機
            Phone phone = new ApplePhone();

            // 現在想貼膜了
            Decorator applePhoneWithSticker = new Sticker(phone);
            // 擴展貼膜行為
            applePhoneWithSticker.Print();
            Console.WriteLine("----------------------\n");

            // 現在我想有掛件了
            Decorator applePhoneWithAccessories = new Accessories(phone);
            // 擴展手機掛件行為
            applePhoneWithAccessories.Print();
            Console.WriteLine("----------------------\n");

            // 現在我同時有貼膜和手機掛件了
            Sticker sticker = new Sticker(phone);
            Accessories applePhoneWithAccessoriesAndSticker = new Accessories(sticker);
            applePhoneWithAccessoriesAndSticker.Print();
            Console.ReadLine();
        }

從上面的客戶端代碼可以看出,客戶端可以動態地將手機配件增加到手機上,如果需要添加手機外殼時,此時只需要添加一個繼承Decorator的手機外殼類,從而,裝飾模式擴展性也非常好。

七、裝飾模式的.NET應用

在.NET 類庫中也有裝飾者模式的實現,該類就是System.IO.Stream

MemoryStream memoryStream = new MemoryStream(new byte[] {95,96,97,98,99});

            // 擴展緩衝的功能
            BufferedStream buffStream = new BufferedStream(memoryStream);

            // 添加加密的功能
            CryptoStream cryptoStream = new CryptoStream(memoryStream,new AesManaged().CreateEncryptor(),CryptoStreamMode.Write);
            // 添加壓縮功能
            GZipStream gzipStream = new GZipStream(memoryStream, CompressionMode.Compress, true);

八、總結

裝飾者模式本質上來說是AOP思想的一種實現方式,其持有被裝飾者,因此可以控制被裝飾者的行為從而達到了AOP的效果。

要點:
1:繼承屬於擴展形式一種,但不見的是達到彈性設計的最佳方式,組合優於繼承。
2:應該允許行為可以被拓展,而無需修改現有的代碼。
3:裝飾者模式意味著一群裝飾者類,這些類用來包裝具體組件。
4:裝飾者類反映出被裝飾組件類型。
5:可以使用無數個裝飾者包裝一個組件。
6:裝飾者會導致設計中出現許多小對象,如果過度使用,會讓程式變得很複雜。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前段時間做了微信自定義分享的功能,分享出的頁面存在邀請碼在ios手機上複製失敗的問題,然而在PC端和安卓機上是沒有問題的。百度了一下,基本給出的解決方案是:ios不單純支持on,為點擊的元素添加空點擊事件:onclick="",眾說芸芸,但是試了下沒一個有用! 如果你複製的文本內容來自是input的 ...
  • 題目 169. 求眾數 給定一個大小為 n 的數組,找到其中的眾數。眾數是指在數組中出現次數大於 ⌊ n/2 ⌋ 的元素。 你可以假設數組是非空的,並且給定的數組總是存在眾數。 示例 1: 示例 2: 眾數(Mode)是統計學名詞,在統計分佈上具有明顯集中趨勢點的數值,代表數據的一般水平(眾數可以不 ...
  • 一、什麼是橋接模式 橋接模式(Bridge Pattern):將抽象部分與它的實現部分分離,使它們都可以獨立地變化。它是一種對象結構型模式,又稱為柄體(Handle and Body)模式或介面(Interface)模式。 二、橋接模式的結構 在橋接模式結構圖中包含如下幾個角色: ●Abstract ...
  • 如果第二次看到我的文章,歡迎右側掃碼訂閱我喲~ 👉 本文長度為5389字,建議閱讀14分鐘。 堅持原創,每一篇都是用心之作~ 沒想到這篇文章寫了這麼長,一時半會沒消化完的話,可以收藏一下先。 這是「伸縮性」章節的第四篇,先給新來的小伙伴們簡單回顧下前三篇的內容。 做「伸縮性」最重要的就是先做好「無 ...
  • Gobelieve 架構 Gobelieve github地址 im 客戶連接伺服器 (可分散式部署,暫無負載均衡模塊) imr 路由查詢伺服器(主要解決im分散式部署的問題) ims 存儲伺服器 (主從部署) 基礎模塊 1.數據包協議 包:header(12)|body header:len(4) ...
  • 很久之前的讀書整理內容,躺在草稿里很久了。。。 一、 "開放-封閉"原則(OCP) Open-Closed Principle原則講的是:一個軟體實體應當對擴展開放,對修改關閉。 優點: 通過擴展已有軟體系統,可以提供新的行為,以滿足對軟體的新的需求,使變化中的軟體有一定的適應性和靈活性。 已有軟體 ...
  • 環境搭建 使用的Eclipse版本: Oxygen.1a Release (4.7.1a) Build id: 20171005 1200, 通過添加 "Xtext Download" 上列出的Releases update site安裝xtext IDE和xtext SDK. 之後打開Eclips ...
  • 李際軍老師“面向對象程式設計”課程第一課筆記整理 面向對象程式設計概述 20世紀90年代以來面向對象程式設計(Object Oriented Programming, 簡稱OOP) 迅速地在全世界流行, 並一躍成為程式設計的主流技術。 現在,面向對象程式設計的思想已經被越來越多的軟體設計人員所接受, ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...