【設計模式】裝飾模式
裝飾模式
簡介
裝飾模式可以在不改變一個物件本身功能的基礎上給物件增加額外的新行為。裝飾模式是一種用於替代繼承的技術。通過一種無須定義子類的方式給物件動態增加職責,使用物件之間的關聯關係取代類之間的繼承關係。裝飾模式中引入了裝飾類,在裝飾類中既可以呼叫待裝飾的原有物件的方法,還可以增加新的方法,以擴充原有類的功能。
裝飾模式:動態地給一個物件增加一些額外的職責。就拓展功能而言,裝飾模式提供了一種比使用子類更加靈活的替代方案。
結構
實現
實現方式:
- 確保業務邏輯可用一個基本組建以及多個額外可選層次表示。
- 找出基本組建和可選層次的通用方法。建立一個元件介面並在其中宣告這些方法。
- 建立一個具體元件類,並定義其基礎行為。
- 建立修飾基類,使用一個成員變數儲存指向被封裝物件的引用。該成員變數必須被宣告為元件介面型別,從而能在執行時連線具體元件和裝飾。裝飾基類必須將所有工作委派給被封裝的物件。
- 確保所有類實現元件介面。
- 將裝飾基類拓展為具體裝飾。具體裝飾必須在呼叫父類(總是委派給被封裝物件)之前或之後執行自身的行為。
- 客戶端程式碼負責建立裝飾並將其組合成客戶端所需的形式。
#include <iostream> // 抽象部(構)件 class Component { public: virtual ~Component() {} virtual std::string Operation() const = 0; }; // 具體部(構)件 class ConcreteComponent : public Component { public: std::string Operation() const override { return "ConcreteComponent"; } }; // 抽象裝飾 class Decorator : public Component { protected: Component* component_; public: Decorator(Component* component) : component_(component) {} std::string Operation() const override { return this->component_->Operation(); } }; // 具體裝飾A class ConcreteDecoratorA : public Decorator { public: ConcreteDecoratorA(Component* component) : Decorator(component) {} std::string Operation() const override { return "ConcreteDecoratorA(" + Decorator::Operation() + ")"; } }; // 具體裝飾B class ConcreteDecoratorB : public Decorator { public: ConcreteDecoratorB(Component* component) : Decorator(component) {} std::string Operation() const override { return "ConcreteDecoratorB(" + Decorator::Operation() + ")"; } }; void ClientCode(Component* component) { // ... std::cout << "RESULT: " << component->Operation() << std::endl; // ... } int main(int argc, char *argv[]) { Component* simple = new ConcreteComponent; std::cout << "Client: I've got a simple component:\n"; ClientCode(simple); std::cout << "\n"; // 為simple裝飾上ConcreteDecoratorA和ConcreteDecoratorB Component* decorator1 = new ConcreteDecoratorA(simple); Component* decorator2 = new ConcreteDecoratorB(decorator1); std::cout << "Client: Now I've got a decorated component:\n"; ClientCode(decorator2); std::cout << "\n"; delete simple; delete decorator1; delete decorator2; return 0; }
# -*- coding: utf-8 -*- class Component(): """ """ def operation(self) -> str: pass class ConcreteComponent(Component): """ """ def operation(self) -> str: return "ConcreteComponent" class Decorator(Component): """ """ _component: Component = None def __init__(self, component: Component) -> None: self._component = component @property def component(self) -> str: return self._component def operation(self) -> str: return self._component.operation() class ConcreteDecoratorA(Decorator): """ """ def operation(self) -> str: return f"ConcreteDecoratorA({self.component.operation()})" class ConcreteDecoratorB(Decorator): """ """ def operation(self) -> str: return f"ConcreteDecoratorB({self.component.operation()})" def client_code(component: Component) -> None: """ """ # ... print(f"RESULT: {component.operation()}", end="") # ... if __name__ == "__main__": simple = ConcreteComponent() print("Client: I've got a simple component:") client_code(simple) print("\n") # 為simple裝飾上ConcreteDecoratorA和ConcreteDecoratorB decorator1 = ConcreteDecoratorA(simple) decorator2 = ConcreteDecoratorB(decorator1) print("Client: Now I've got a decorated component:") client_code(decorator2)
例項
問題描述
同上。
問題解答
同上。
總結
優點
- 無需建立新子類即可擴充套件物件的行為。
- 可以在執行時新增或刪除物件的功能。
- 可以用多個裝飾封裝物件來組合幾種行為。
- 單一職責原則。可以將實現了許多不同行為的一個大類拆分為多個較小的類。
- 開閉原則。具體構件類和具體裝飾類可以獨立變化和擴充套件。
缺點
- 在封裝器棧中刪除特定封裝器比較困難。
- 實現行為不受裝飾棧順序影響的裝飾比較困難。
- 各層的初始化配置程式碼看上去可能會很糟糕。
場景
- 如果你希望在無需修改程式碼的情況下即可使用物件,且希望在執行時為物件新增額外的行為,可以使用該模式。
- 如果用繼承來擴充套件物件行為的方案難以實現或者根本不可行,你可以使用該模式。
與其他模式的關係
-
介面卡模式可以對已有物件的介面進行修改,裝飾模式則能在不改變物件介面的前提下強化物件功能。此外,裝飾還支援遞迴組合,介面卡則無法實現。
-
介面卡能為被封裝物件提供不同的介面,代理模式能為物件提供相同的介面,裝飾則能為物件提供加強的介面。
-
責任鏈模式和裝飾模式的類結構非常相似。兩者都依賴遞迴組合將需要執行的操作傳遞給一系列物件。但是,兩者有幾點重要的不同之處。責任鏈的管理者可以相互獨立地執行一切操作,還可以隨時停止傳遞請求。另一方面,各種裝飾可以在遵循基本介面的情況下擴充套件物件的行為。此外,裝飾無法中斷請求的傳遞。
-
組合模式和裝飾模式的結構圖很相似,因為兩者都依賴遞迴組合來組織無限數量的物件。裝飾類似於組合,但其只有一個子元件。此外還有一個明顯不同:裝飾為被封裝物件添加了額外的職責,組合僅對其子節點的結果進行了“求和”。但是 模式也可以相互合作:你可以使用裝飾來擴充套件組合樹中特定物件的行為。
-
大量使用組合和裝飾的設計通常可從對於原型模式的使用中獲益。你可以通過該模式來複制複雜結構,而非從零開始重新構造。
-
裝飾模式可讓你更改物件的外表,策略模式則讓你能夠改變其本質。
-
裝飾和代理有著相似的結構,但是其意圖卻非常不同。這兩個模式的構建都基於組合原則,也就是說一個物件應該將部分工作委派給另一個物件。兩者之間的不同之處在於代理通常自行管理其服務物件的生命週期,而裝飾的生成則總是由客戶端進行控制。