裝飾器模式( Decorator Pattern ): 代理模式的雙胞胎兄弟

  1. 參考書籍: 《Design Patterns: Elements of Reusable Object-Oriented Software》

設計模式用前須知

  • 設計模式種一句出現頻率非常高的話是,“ 在不改動。。。。的情況下, 實現。。。。的擴展“ 。
  • 對於設計模式的學習者來說,充分思考這句話其實非常重要, 因爲這句往往只對框架/ 工具包的設計纔有真正的意義。因爲框架和工具包存在的意義,就是爲了讓其他的程序員予以利用, 進行功能的擴展,而這種功能的擴展必須以不需要改動框架和工具包中代碼爲前提
  • 對於應用程序的編寫者, 從理論上來說, 所有的應用層級代碼至少都是處於可編輯範圍內的, 如果不細加考量, 就盲目使用較爲複雜的設計模式, 反而會得不償失, 畢竟靈活性的獲得, 也是有代價的。

裝飾器模式 ( Decorator )

  • 設計意圖

    • GOF: 將一些職責動態地添加到一個對象上。 裝飾器模式提供了一種不創建新的子類, 而靈活擴展對象功能的解決方案。
    • 關鍵詞: “動態添加職責” 、 “不創建新的子類”
  • GOF舉例:

    • 有些時候我們希望給單個的對象增加一些功能或職責, 而不是給整個類添加職責。 例如對於一個圖形界面工具包而言, 應該允許用戶向各種界面組件添加邊框和滾動的功能。
    • 實現這個目標的一種方案是利用繼承。
      • 當一個類需要增加邊框時, 就繼承Border 類。 但是這樣是很不靈活的, 因爲哪些類能有邊框被靜態地(編譯時刻)固定好了, 用戶沒有辦法控制什麼時候一個組件可以裝飾一個邊框。
    • 更加靈活的一種方案是裝飾器模式
      • 把需要增加邊框的類封裝到另一個負責添加邊框的對象中。 這個起封裝作用的對象被叫做裝飾器。
      • 爲了使得裝飾器對於用戶代碼透明, 裝飾器需要符合被裝飾對象的接口。 裝飾器會將發送給 被裝飾對象 的請求轉發給 被裝飾對象, 在此基礎上, 可能會進行一些額外的操作( 如在轉發前或轉發後添加邊框) 。 這種透明性可以允許你不斷遞歸地嵌套裝飾器, 從而添加無上限個數的功能。
        這裏寫圖片描述
    • 以上圖爲例, 假設我們擁有一個文本視圖, 該視圖在一個窗口內顯示文本。 文本視圖默認沒有滾動條, 因爲我們不一定總是需要滾動條。 當我們需要滾動條時, 我們可以用一個 ScrollDecorator 去添加它。
    • 假設我們還需要在文本框外側添加一個粗黑的邊框, 我們可以使用 BorderDecorator 去添加它。
    • 我們可以簡單地把各種裝飾器與 TextView 組合來產生所期望的結果。
      這裏寫圖片描述

這裏寫圖片描述

圖例說明


在這裏插入圖片描述
在這裏插入圖片描述
這種關係在 java 語言中, 可以按照如下形式實現(不止這一種實現形式)

abstract class A {
    private B b;
}

在這裏插入圖片描述
這種關係在 java 語言中, 可以按照如下形式實現(不止這一種實現形式)

abstract class A {
    private List<B> b;
}

注意: A, B 的類型比較靈活, 可以是 class, abstract class , interface 的任意一種類型, 使用哪種完全看需求


在這裏插入圖片描述
這種關係在 java 語言中, 可以按照如下形式實現

class A extends B{
}

class A implements B{
}

在這裏插入圖片描述

圖例分析

  • VisualComponent 是可顯示的組件對象的抽象類。 它定義了組件的繪製和事件響應操作(圖例中沒有畫事件響應相關的操作)
  • 注意觀察圖例中的 BoderDecorator , 可以看出來, 裝飾器是如何轉發請求給被裝飾對象的:
    • BorderDecorator 中的 draw() 操作會先直接調用Decorator 的 draw() 方法, 再調用自定義的 drawBorder( )。 而 Decorator 的 draw() 方法則又是直接調用被裝飾的 Component 的 draw 方法
  • Decorator 的子類可以自由地爲特定的功能添加操作。

裝飾器模式與組合模式對比

  • 如果仔細觀察 【裝飾器模式】 的結構和 【組合模式】 的結構, 會發現他們非常相似

    • 裝飾器模式
      這裏寫圖片描述
    • 組合模式
      這裏寫圖片描述
  • 事實是他們的確非常相似

    • 一個 裝飾器 (Decorator) 可以被看做是一個退化的, 僅僅擁有一個組件的 複合實體(Composite)(但如此看待, 便會誤解裝飾器模式的核心點)。
    • 裝飾器模式和組合模式都以遞歸的方式組合數量不限的對象。
  • 然而, 他們之間的區別也很明確。

    • 裝飾器會增加一些附加的功能
      • 圖例中, ConcreteDecorator 中的的operation 在調用component 的 operation() 之後調用了addedBehavior()
    • 而組合模式中的對象聚合並不會增加額外的功能。
      • 圖例中, Composite 的 operation 方法中只是循環調用了所有 children 的 operation 方法 。
  • 從根本上來說, 組合模式和裝飾器模式最大的區別就是其設計意圖的區別。

    • 裝飾器模式的設計意圖是了在不添加子類的前提下, 爲一個對象增添功能或職責。
    • 組合模式則專注於把相關的類組織起來, 使得編程者可以用相同的方式去操作組裝出來的複合對象和未被組裝的單體對象。
  • 組合模式和裝飾器模式的設計意圖雖然不同, 但卻是互補的。 這使得他們往往被搭配在一起使用。 呈現如下的結構

這裏寫圖片描述

  • 注意點
    • Decorator 都繼承(或者實現)相同的抽象類(或接口) Component
    • Leaf 負責實現一些系統中最基礎的類, 例如圖形應用中的 Line, Circle, Rectangle
    • 從裝飾器模式的角度看, 此時的一個複合實體 Composite 是一個可以被裝飾的 ConcreteComponent.
    • 從組合模式的角度看, 此時的一個裝飾器 Decorator , 是一個可以被組裝的單體對象 LeafComponent

裝飾器模式與代理模式對比

  • 如果說裝飾器模式和組合模式還只是結構相似的話, 那代理模式和裝飾器模式的結構幾乎都是一模一樣了

  • 代理模式

這裏寫圖片描述

這裏寫圖片描述

  • 裝飾器模式

這裏寫圖片描述

這裏寫圖片描述

  • 可以看到, 如果從結構或者實現的角度來講, 代理模式和裝飾器模式完全一樣, 都會持有一個對被代理(被裝飾)對象的引用, 然後轉發請求給被持有的對象。

  • 那麼, 裝飾器模式和代理模式如何區分

    • 以**【使用目的】** 區分
      • 代理模式的目的在於進行 訪問控制, 其意圖在於爲被代理對象提供一個替身, 而之所以提供替身而不直接訪問的目的是, 原有的被代理對象可能不便於被直接訪問(是一個遠程對象), 或者是一個訪問權限需要被限制的對象。
      • 裝飾器模式的目的在於進行功能添加 / 功能增強 , 其意圖在於爲被裝飾對象動態地增加功能或屬性。
    • 【使用方式】 區分
      • 裝飾器模式中的裝飾器 Decorator 支持迭代式嵌套
      • 代理模式中的代理 Proxy 從設計邏輯上來講, 是不應該 被迭代式嵌套的
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章