简介
- 动态地给一个对象添加一些额外的职责。
- 就增加功能来说,装饰模式比生成子类更为灵活。
动机
- 有时我们希望给某个对象而不是整个类添加一些功能。
- 使用继承机制是添加功能的一种有效途径,但不够灵活,用户不能控制对组件添加功能的方式和时机。
- 一种较为灵活的方式是将组件嵌入另一个对象中,由这个对象添加功能,我们称这个嵌入的对象为装饰。
- 这个装饰与它所装饰的组件接口一致,因此它对使用该组件的客户透明。
适用性
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
- 处理那些可以撤销的职责。
- 当不能采用生成子类的方法进行扩充时。
- 可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。
- 类定义被隐藏,或类定义不能用于生成子类。
结构
装饰模式的参与者
- Component
- 定义一个对象接口,可以给这些对象动态地添加职责。
- ConcreteComponent
- 定义一个对象,可以给这个对象添加一些职责。
- Decorator
- 维持一个指向Component对象的指针,并定义一个与Component接口一致的接口。
- ConcreteDecorator
- 向组件添加职责。
装饰模式的协作
- Decorator将请求转发给它的Component对象,并有可能在转发请求前后执行一些附加动作。
效果
- 比静态继承更灵活
- 避免在层次结构高层的类有太多的特征
- Decorator与它的Component不一样
- 有许多小对象
注意点
- 如果只有一个ConcreteComponent类而没有Component类,那么Decorator类可以是ConcreteComponent的一个子类。
- 同理,如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而可以把Decorator和ConcreteDecorator合并成一个类。
实例
给定两种初始的汽车类,例如丰田和沃尔沃,利用装饰模式分别给它们添加新的功能,其中丰田可以导航和自动驾驶,沃尔沃可以导航和语音控制。
UML图
代码与函数
Component:Car类
ConcreteComponent:TOYOTA、VOLOVO类
Decorator:Function类
ConcreteDecorator:Navigator、SelfDrive、VoiceControl类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
#include<iostream> using namespace std; /* 给定两种初始的汽车类,例如丰田和沃尔沃, 利用装饰模式分别给它们添加新的功能, 其中丰田可以导航和自动驾驶,沃尔沃可以导航和语音控制。 */ class Car{//Component public: virtual void showInfo() = 0; }; class TOYOTA: public Car{ public: TOYOTA(){} TOYOTA(string name){ this->name = name; } void showInfo(){ cout<<name<<endl; } private: string name; }; class VOLOVO: public Car{ public: VOLOVO(){} VOLOVO(string name){ this->name = name; } void showInfo(){ cout<<name<<endl; } private: string name; }; class Function: public Car{//Decorator protected: Car* car; public: void Decorate(Car* car){ this->car = car; } void showInfo(){ if(car != NULL)car->showInfo(); } }; //其中丰田可以导航和自动驾驶,沃尔沃可以导航和语音控制。 class Navigator: public Function{ public: void showInfo(){ cout<<"导航 "; Function::showInfo(); } }; class SelfDrive: public Function{ public: void showInfo(){ cout<<"自动驾驶 "; Function::showInfo(); } }; class VoiceControl: public Function{ public: void showInfo(){ cout<<"语音控制 "; Function::showInfo(); } }; //其中丰田可以导航和自动驾驶,沃尔沃可以导航和语音控制。 int main(){ Car* toyota = new TOYOTA("丰田卡罗拉"); Navigator* na = new Navigator(); SelfDrive* sd = new SelfDrive(); na->Decorate(toyota); sd->Decorate(na); sd->showInfo(); cout<<endl; Car* volovo = new VOLOVO("沃尔沃S90"); Navigator* na2 = new Navigator(); VoiceControl* vc = new VoiceControl(); na2->Decorate(volovo); vc->Decorate(na2); vc->showInfo(); return 0; } |
总结
①本次实验通过Car的实例掌握并编码了装饰模式。
②注意如果只有一个ConcreteComponent类而没有Component类,那么Decorator类可以是ConcreteComponent的一个子类。在本次实验中,有TOYOTA和VOLOVO两个具体的类,因此需要一个Component的Car类来定义接口。
③由②可知,如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而可以把Decorator和ConcreteDecorator合并成一个类。在本次实验中有Navigator、SelfDrive、VoiceControl三个具体的装饰类。
④在客户端中,要最后一个添加的职责,用来是包装好了的车对象,因此需要由最后一个装饰的实例显示结果。