設計模式之裝飾器模式(decorator pattern)

来源:https://www.cnblogs.com/yssjun/archive/2019/06/30/11110013.html
-Advertisement-
Play Games

裝飾器模式主要對現有的類對象進行包裹和封裝,以期望在不改變類對象及其類定義的情況下,為對象添加額外功能。是一種對象結構型模式。需要註意的是,該過程是通過調用被包裹之後的對象完成功能添加的,而不是直接修改現有對象的行為,相當於增加了中間層。類似於python中的@裝飾器。 下麵還是按照老規矩,先來瞭解 ...


裝飾器模式主要對現有的類對象進行包裹和封裝,以期望在不改變類對象及其類定義的情況下,為對象添加額外功能。是一種對象結構型模式。需要註意的是,該過程是通過調用被包裹之後的對象完成功能添加的,而不是直接修改現有對象的行為,相當於增加了中間層。類似於python中的@裝飾器。

下麵還是按照老規矩,先來瞭解一下該模式相關的概念和原理,然後通過兩個具體的實例體會一下如何在實際開發中應用該模式。

1. 目的

可以動態的為同一類的不同對象加以修飾以添加新的功能。

2. 動機

靈活的對類對象功能進行擴展。

3. 優缺點

優點:

  1. 相比較於類的繼承來擴展功能,對對象進行包裹更加的靈活;
  2. 裝飾類和被裝飾類相互獨立,耦合度較低;

缺點:

  1. 沒有繼承結構清晰;
  2. 包裹層數較多時,難以理解和管理;

4. 應用場景

  • 動態的增加對象的功能;
  • 不能以派生子類的方式來擴展功能;
  • 限制對象的執行條件;
  • 參數控制和檢查等;

5.  原理

下麵是GoF介紹的典型的裝飾器模式的UML類圖:

Component:

 對象的介面類,定義裝飾對象和被裝飾對象的共同介面;

ConcreteComponent:

 被裝飾對象的類定義;

Decorator:

 裝飾對象的抽象類,持有一個具體的被修飾對象,並實現介面類繼承的公共介面;

ConcreteDecorator:

 具體的裝飾器,負責往被裝飾對象添加額外的功能;

說明:

 由於這個模式從實際的例子來理解更加的直觀方便,因此這裡不再單獨的實現上面的UML結構代碼。

6.實例——畫圖

先來通過一個簡單的畫圖的實例來直觀感受一下。

前提:

系統中存在一個畫圓的類,該類只是用來畫圓,以及其他一些大小和位置等參數的控制。

新加需求:

  • 可以對圓的邊進行著色
  • 可以對圓填充顏色;
  • 可以同時對邊和內部著色;

這個需求的常規方法實現可能如下:

  1. 對畫圓類進行迭代,以支持邊和內部顏色填充 ;
  2. 畫圓類作為父類,分別定義三個子類,繼承父類的畫圓方法,子類分別實現對應的作色需求;

上面的兩個方法都是可行的,也是比較直觀的,這裡我們嘗試使用裝飾器模式來實現,作為以上兩種方法的對比。

下麵來看一下裝飾器模式實現該需求的UML類圖:

介面類:shape

public interface Shape {
    void draw();
}

 畫圓類:Circle

public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.print("a circle!");
    }
}

抽象裝飾器類:Decorator

public abstract class Decorator implements Shape {
    
    protected Shape circle;
    
    public Decorator(Shape shape) {
        circle = shape;
    } 
    
    public void draw() {
        circle.draw();
    }
}

為圓邊著色裝飾器類:CircleEdge

public class CircleEdge extends Decorator {
    
    public CircleEdge(Shape circle) {
        super(circle);
    }
    
    private void setEdgeColor() {
        System.out.print(", edge with color");
    }
    
    public void draw() {
        circle.draw();
        setEdgeColor();
    }
}

為圓填充顏色裝飾器類:CircleEdge

public class CircleFill extends Decorator {
    
    public CircleFill(Shape circle) {
        super(circle);
    }
    
    private void setEdgeFill() {
        System.out.print(", content with color");
    }
    
    public void draw() {
        circle.draw();
        setEdgeFill();
    }
}

演示:

public class Demo {
    public static void main(String[] args) {
        Shape circle = new Circle();
        circle.draw();
        System.out.println("");
        Decorator circleEdge = new CircleEdge(circle);
        circleEdge.draw();
        System.out.println("");
        Decorator circleFill = new CircleFill(circle);
        circleFill.draw();
        System.out.println("");
        Decorator circleEdgeFill = new CircleFill(circleEdge);
        circleEdgeFill.draw();
    }
}

結果:

a circle!
a circle!, edge with color
a circle!, content with color
a circle!, edge with color, content with color

 上面我們通過實現兩個裝飾器分別完成對邊著色和填充的需求,通過對裝飾器的進一步裝飾,我們完成了同時著色的需求。

7.實例——網路數據報封裝

接下來我們在使用網路數據傳輸的例子來體會一下裝飾器模式,下圖表示的是應用層的文件傳輸協議FTP通過TCP來傳輸數據:

雖然應用層可以越過傳輸層直接使用網路層進行數據發送(如,ICMP),但多數都會使用傳輸層的TCP或者UDP進行數據傳輸的。

下麵我們用裝飾器模式來表示一下應用層數據通過傳輸層來發送數據,UML類圖如下:

上述圖中表示了,應用層的數據通過添加TCP頭或者UDP頭,然後通過下麵的網路層send數據。

 數據報介面類:Datagram

public interface Datagram {
    void send();    // 通過網路層發送IP數據報
}

應用層數據類:AppDatagram

public class AppDatagram implements Datagram {
    @Override
    public void send() {
        System.out.print("send IP datagram!");
    }
}

傳輸層類(抽象裝飾器):TransportLayer

public abstract class TransportLayer implements Datagram {
    
    protected Datagram appData;
    
    public TransportLayer(Datagram appData) {
        this.appData = appData;
    } 
    
    public void send() {
        appData.send();
    }
}

添加TCP頭部類:UseTCP

public class UseTCP extends TransportLayer {
    
    public UseTCP(Datagram appData) {
        super(appData);
    }
    
    private void addHeader() {
        System.out.print("Appdata add TCP header, ");
    }
    
    public void send() {
        addHeader();
        appData.send();
    }
}

添加TCP頭部類:UseUDP

public class UseUDP extends TransportLayer {
    
    public UseUDP(Datagram appData) {
        super(appData);
    }
    
    private void addHeader() {
        System.out.print("Appdata add UDP header, ");
    }
    
    public void send() {
        addHeader();
        appData.send();
    }
}

演示:

public class Demo {
    public static void main(String[] args) {
        Datagram appData = new AppDatagram();
        appData.send();
        System.out.println("");
        TransportLayer tcpData = new UseTCP(appData);
        tcpData.send();
        System.out.println("");
        TransportLayer udpData = new UseUDP(appData);
        udpData.send();
        System.out.println("");
    }
}

結果:

send IP datagram!
Appdata add TCP header, send IP datagram!
Appdata add UDP header, send IP datagram!

當然這裡例子中已經添加過TCP頭部的數據報不能再使用UDP傳輸了,無意義,也被必要。

8. 總結

其實所謂裝飾器,本質上是對現有類對象的包裹,得到一個加強版的對象。

和python中@裝飾器不同的是:

  1. python中的裝飾器是作用於函數或者類定義的,並直接覆蓋掉了原來函數或者類的定義;
  2. 裝飾器模式僅僅是修改了了已經產生的對象的行為,和類定義沒有半點關係;

通過上面的兩個例子,應該對裝飾器模式有了一個簡單的認識。

另外,要體會到什麼時候用繼承什麼時候用裝飾器。

參考:

GoF《Design Patterns: Elements of Reusable Object-Oriented Software》

https://www.runoob.com/design-pattern/decorator-pattern.html


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

-Advertisement-
Play Games
更多相關文章
  • 一、nrm: nrm是專門用來管理和快速切換私人配置的registry; nrm提供了一些最常用的npm包鏡像地址,能夠讓我們快速的切換安裝包時候的伺服器地址; 二、鏡像: 原來 包 剛一開始是只存在於國外的NPM伺服器,但是由於網路原因,經常訪問不到,這時候,我們可以在國內,創建一個和官網完全一樣 ...
  • 相信不少朋友閑的時候就會使用金山打字通進行打字練習,昨天突發奇想,如何在打字過程中,也屬性前端的關鍵字,所有就收集到了一些前端的關鍵字。我知道現在的編輯器都有提示功能,但是,也不能過分的依賴編輯器。 除空格外,共1347個字元,建議分成兩個部分進行練習: www url http W3C html ...
  • 做前端設計時,通常需要控制字元顯示的寬度或者行數,多餘字元通常以“...”替代;本文分兩點情況來進行設置: 1、需要字元保持固定寬度,其餘字元顯示省略號(‘...’); 2、需要字元顯示固定行數(本文以三行為例),其餘字元顯示省略號(‘...’); 以上方法基本可以滿足需求。當使用第二種情況(多行隱 ...
  • 一、三者之間的對比: 1、methods方法表示一個具體的操作,主要書寫業務邏輯; 2、watch;一個對象,鍵是需要觀察的表達式,值是對應回調函數。主要用來監聽某些特定數據的變化,從而進行某些具體業務邏輯操作;可以看作是”computed"和“methods”的結合體; 3、computed屬性的 ...
  • 一. HTML介紹: HTML是什麼? 超文本標記語言(Hypertext Markup Language),是一種用於創建網頁的標記語言,不是編程語言 本質上是瀏覽器可識別的規則,我們按照規則寫網頁,瀏覽器根據規則渲染我們的網頁.對於不同的瀏覽器,對同一個標簽可能會有不同的解釋. (相容性問題) ...
  • 下拉框等需要顯示上下箭頭切換的CSS樣式 1 .icon-right, .icon-down, .icon-up { 2 display: inline-block; 3 padding-right: 13rpx; 4 position: absolute; 5 /*組件內調整箭頭的位置*/ 6 r ...
  • 概述 本來,數據存儲都是由 cookie 完成的,但是 cookie 不適合大量數據的存儲,cookie 速度慢且效率低。 現在,HMLT5提供了兩種在客戶端存儲數據的辦法: 兩者之間的實測對比 localStorage 首先我們先運行這段代碼: 然後瀏覽器則彈出顯示: 接著,我們註釋掉localS ...
  • 目錄如下 1. 軟體架構的進化 2. 微服務的優勢和不足 3. 微服務架構所帶來的問題及解決方案 1.軟體架構的進化 於筆者經歷來看 架構大致從 單體架構 》MVC 》 微服務 單體架構 單體架構特點在於所有功能業務打包在一個發佈包里,部署在一個web容器中,運行在一個進程里。單體架構的優點在於 容 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...