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; //显示 }; //***********************具体组件角色******************* //PrintDialog class 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)装饰模式与模板方法模式
两者的功能有点相似,模板方法主要应用在算法骨架固定的情况。如果是一个相对动态的算法,可以使用装饰模式,因为用装饰器组装时,其实也相当于一个调用算法的步骤,相当于一个动态的算法骨架。