標籤:
1. 裝飾模式(Decorator Pattern)的定義
(1)動態地給一個對象添加一些額外的職責。就增加功能來說,裝飾模式比產生子類更為靈活。
①裝飾模式是為對象(而不是類)添加功能的。
②用組合方式,而不是繼承方式為對象添加功能。
(2)裝飾模式的結構和說明
①Component:組件對象的介面,可以給這些對象動態地添加職責。
②ConcreteComponent:具體的組件對象,實現組件對象介面,通常就是被裝飾器裝飾的原始對象,也就是可以給這個對象添加職責。
③Decorator:所有裝飾器的抽象父類,需要定義一個與組件介面一致的介面,並持有一個Component對象的指標,其實就是持有一個被裝飾的對象。之所以從Decorator繼承而來,除了與被裝飾對象介面相同,還是有個原因是Decorator本身也可以被進一步的裝飾,形成多層的裝飾。注意,裝飾後仍然是一個Component對象,而且其功能更為複雜。
④ConcreteDecorator:實際的裝飾器對象,實現具體要向被裝飾對象添加的功能。
(3)思考裝飾模式
①裝飾模式的本質:動態組合。動態是手段,組合才是目的。通過對象組合(而不是繼承)來實現為被裝飾對象透明的增加功能,而且也可以控制功能的訪問。
②裝飾模式的動機:由於繼承為類型引入的靜態特質,使得通過繼承擴充方式缺乏靈活性,並且隨著子類的增多,會導致子類的膨脹。而裝飾模式可以根據需要來動態地使“對象功能擴充”,避免子類膨脹問題。
③裝飾器:不僅僅可以給被裝飾對象增加功能,還可以根據需要選擇是否調用被裝飾對象的功能。如果不調用,那就變成完全重新實現,相當於動態修改了被裝飾對象的功能。同時裝飾器一定要實現和組件類一致的介面,保證它們是同一個類型,並具有同一個外觀,這樣組合完成的裝飾才能夠遞迴調用下去。
2. 裝飾模式的優缺點
(1)優點
①比繼承更靈活:繼承是靜態,而裝飾模式把功能分離到每個裝飾器,然後通過組合方式,在運行時動態地組合功能。
②更容易複用功能:一般一個裝飾器只實現一個功能,使實現裝飾器變得簡單,更重要的是這樣有利於裝飾器功能的複用,可以給一個對象增加多個不同裝飾器,也可以用一個裝飾器不同的對象,從而實現複用裝飾器的功能。
③簡化高層定義:通過組合裝飾器的方式,在進行高層定義的時候,不用把所有功能都定義出來,而是定義最基本的就可以了,在需要的時候,組合相應的裝飾器來完成所需的功能。
(2)缺點
①會產生很多細粒度的對象
②多層的裝飾是比較複雜的,應盡量減少裝飾類的資料,以便降低系統的複雜度。
3. 使用情境
(1)在不影響其他對象的情況下,以動態、透明的方式給對象添加職責。
(2)不適合使用子類來進行擴充時,可以考慮使用裝飾模式。因為裝飾模式是使用“對象組合”的方式。所謂不適合用子類擴充的方式,比如擴充功能需要的子類太多,造成子類數目呈爆炸性增長。
(3)需要為一批兄弟類進行改裝或加裝功能。可以選用裝飾模式。
【編程實驗】在顯示每個Window前追加顯示某個logo的功能。
//結構型模式:裝飾模式//情境:在顯示每個Window前追加顯示某個logo的功能。#include <iostream>using namespace std;//************************抽象組件類******************class BaseWin //Component{public: virtual void show() = 0; //顯示};//***********************具體組件角色*******************//PrintDialogclass PrintDialog : public BaseWin {public: void show() { cout << "PrintDialog" << endl; } };//WinConfig:配置視窗class WinConfig : public BaseWin {public: void show() { cout << "WinConfig" << endl; } };//MainWin:主視窗class MainWin : public BaseWin {public: void show() { cout << "MainWin" << endl; } };//*****************************裝飾器類**********************class DecoratorWin : public BaseWin //繼承,如此裝飾後仍是BaseWin類{private: BaseWin* mWin; //持有一個被裝飾對象的指標public: DecoratorWin(BaseWin* Win){this->mWin = Win;} void show() { mWin->show(); }};//具體的裝飾器class LogWin : public DecoratorWin{private: void displayLogo() {cout << "LogoWin Showing..." << endl;} public: LogWin(BaseWin* win) : DecoratorWin(win){} void show() { displayLogo(); //增加功能,在原視窗顯示之前,先顯示Logo視窗 DecoratorWin::show(); } };int main(){ //用戶端調用 //顯示主視窗前先顯示Logo視窗 BaseWin* mainWin= new MainWin(); BaseWin* logo = new LogWin(mainWin); logo->show(); //顯示列印對話方塊前先顯示Logo視窗 BaseWin* printDlg = new PrintDialog(); BaseWin* logo2 = new LogWin(printDlg); logo2->show(); delete mainWin; delete logo; delete printDlg; delete logo2; return 0;}
4. 相關模式
(1)裝飾模式與策略模式
①策略模式也可以實現動態地改變對象的功能,但是策略模式只是一層選擇,也就是根據策略選擇一下具體的實作類別而己。而裝飾模式不是一層,而是遞迴調用,無數層都可以。
②策略模式改變的是原始對象的功能,其改變的是對象的核心。而裝飾器可以看作是一個對象的外殼,改變的是經過前一個裝飾器裝飾後的對象,可改變對象的行為。
③Decorator模式僅從外部改變對象,因此對象無需對它的裝飾有任何瞭解;也就是裝飾對象該對象來說是透明的。但策略模式,Component組件本身知道可能進行哪些擴充,因此它必須引用並維護相應的策略。
④策略的方法可能需要修改Component組件以適應新的擴充,另一方面,一個策略可以有自己特定的介面,而裝飾的介面則必須與組件介面一致。
(2)裝飾模式與模板方法模式
兩者的功能有點相似,模板方法主要應用在演算法骨架固定的情況。如果是一個相對動態演算法,可以使用裝飾模式,因為用裝飾器組裝時,其實也相當於一個調用演算法的步驟,相當於一個動態演算法骨架。
第11章 結構型模式—裝飾模式