装饰( Decorator )模式又叫做包装模式。通过一种对客户端透明的方式来扩展对象的功能,是继承关系的一个替换方案。
装饰模式就是把要添加的附加功能分别放在单独的类中,并让这个类包含它要装饰的对象,当需要执行时,客户端就可以有选择地、按顺序地使用装饰功能包装对象。
在装饰模式中的各个角色有:
抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。
装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
具体装饰(Concrete Decorator)角色:负责给构件对象"贴上"附加的责任。
实例一
1 #include <string> 2 #include <iostream> 3 #include <memory> 4 using namespace std; 5 6 //抽象类Tank 抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。 7 class Tank 8 { 9 public: 10 virtual void shot()=0; 11 virtual void run()=0; 12 13 public: 14 virtual ~Tank() 15 { 16 cout<<"in the destructor of Tank"<<endl; 17 } 18 }; 19 //具体类 T50 具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。 20 class T50:public Tank 21 { 22 public: 23 void shot() 24 { 25 cout<<"Tank T50 shot()"<<endl; 26 } 27 void run() 28 { 29 cout<<"Tank T50 run()"<<endl; 30 } 31 public: 32 virtual ~T50() 33 { 34 cout<<"In the destructor of T50"<<endl; 35 } 36 }; 37 //具体类T75 具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。 38 class T75:public Tank 39 { 40 public: 41 void shot() 42 { 43 cout<<"Tank T75 shot()"<<endl; 44 } 45 void run() 46 { 47 cout<<"Tank T75 run()"<<endl; 48 } 49 public: 50 virtual ~T75() 51 { 52 cout<<"In the destructor of T75"<<endl; 53 } 54 }; 55 56 //抽象类,Decorator 装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。 57 class Decorator:public Tank 58 { 59 protected: 60 Tank* tank; 61 public: 62 Decorator(Tank* tank):tank(tank) {} //具体的坦克的装饰类 63 virtual ~Decorator() 64 { 65 cout<<"In the destructor of Decorator"<<endl; 66 } 67 public: 68 void shot() 69 { 70 tank->shot(); 71 } 72 void run() 73 { 74 tank->run(); 75 } 76 }; 77 // 具体装饰(Concrete Decorator)角色:负责给构件对象"贴上"附加的责任 红外线Infrared 。 78 class InfraredDecorator: public Decorator 79 { 80 private: 81 string infrared;//这就是所谓的addAtrribute 82 public: 83 InfraredDecorator(Tank* tank):Decorator(tank) {} 84 virtual ~InfraredDecorator() 85 { 86 cout<<"in the destructor of InfraredDecorator"<<endl; 87 } 88 public: 89 void set_Infrared(const string &infrared) 90 { 91 this->infrared=infrared; 92 } 93 string get_infrared() const 94 { 95 return infrared; 96 } 97 void run() 98 { 99 tank->run(); 100 set_Infrared("+Infrared"); 101 cout<<get_infrared()<<endl; 102 } 103 void shot() 104 { 105 tank->shot(); 106 } 107 }; 108 // 具体装饰 水路两栖Amphibian 109 class AmphibianDecorator:public Decorator 110 { 111 private: 112 string amphibian; 113 public: 114 AmphibianDecorator(Tank* tank):Decorator(tank) {} 115 ~AmphibianDecorator() 116 { 117 cout<<"in the destructor of AmphibianDecorator"<<endl; 118 } 119 public: 120 void set_amphibian(const string &hibian) 121 { 122 this->amphibian=hibian; 123 } 124 string get_amphibian() const 125 { 126 return amphibian; 127 } 128 public: 129 void run() 130 { 131 tank->run(); 132 set_amphibian("+amphibian"); 133 cout<<get_amphibian()<<endl; 134 } 135 void shot() 136 { 137 tank->shot(); 138 } 139 }; 140 141 int main(int argc, char **argv) 142 { 143 //给T50增加红外功能 144 Tank* tank1(new T50); 145 Tank* pid1(new InfraredDecorator(tank1)); 146 pid1->shot(); 147 cout<<endl; 148 pid1->run(); 149 cout<<endl; 150 cout<<endl<<"---------------"<<endl; 151 //给t75增加红外、两栖功能 152 Tank* tank2(new T75); 153 tank2->run(); 154 Tank* pid2(new InfraredDecorator(tank2)); 155 Tank* pad2(new AmphibianDecorator(pid2)); 156 pad2->shot(); 157 cout<<endl; 158 pad2->run(); 159 cout<<endl; 160 cout<<endl<<"--------------"<<endl; 161 162 //动态撤销其他装饰 ? 163 tank2->run(); 164 165 Tank * tank3(tank2); 166 tank3->run(); 167 return 0; 168 }
例程二 :
1 #include <iostream> 2 using namespace std; 3 4 class Car 5 { 6 public: 7 virtual void show() = 0; 8 }; 9 10 class RunCar : public Car 11 { 12 public: 13 virtual void show() 14 { 15 cout << "可以跑" << endl; 16 } 17 protected: 18 private: 19 }; 20 21 22 class SwimCarDirector : public Car 23 { 24 public: 25 SwimCarDirector(Car *car) 26 { 27 m_car = car; 28 } 29 void swim() 30 { 31 cout << "可以游" << endl; 32 } 33 virtual void show() 34 { 35 m_car->show(); 36 swim(); 37 } 38 protected: 39 private: 40 Car *m_car; 41 }; 42 43 class FlyCarDirector : public Car 44 { 45 public: 46 FlyCarDirector(Car *car) 47 { 48 m_car = car; 49 } 50 void fly() 51 { 52 cout << "可以飞" << endl; 53 } 54 virtual void show() 55 { 56 m_car->show(); 57 fly(); 58 } 59 60 private: 61 Car *m_car; 62 }; 63 64 void main() 65 { 66 Car * mycar = NULL; 67 mycar = new RunCar; 68 printf("-----000\n"); 69 mycar->show(); 70 71 printf("-----aaa\n"); 72 73 FlyCarDirector *flycar = new FlyCarDirector(mycar); 74 flycar->show(); 75 76 printf("-----bbb\n"); 77 SwimCarDirector *swimcar = new SwimCarDirector(flycar); 78 swimcar->show(); 79 80 delete swimcar; 81 delete flycar; 82 delete mycar; 83 system("pause"); 84 return ; 85 }
装饰者与适配者模式的区别:
1.关于新职责:适配器也可以在转换时增加新的职责,但主要目的不在此。装饰者模式主要是给被装饰者增加新职责的。
2.关于原接口:适配器模式是用新接口来调用原接口,原接口对新系统是不可见或者说不可用的。装饰者模式原封不动的使用原接口,系统对装饰的对象也通过原接口来完成使用。(增加新接口的装饰者模式可以认为是其变种--“半透明”装饰者)
3.关于其包裹的对象:适配器是知道被适配者的详细情况的(就是那个类或那个接口)。装饰者只知道其接口是什么,至于其具体类型(是基类还是其他派生类)只有在运行期间才知道。
要点:
1. 装饰者和被装饰对象有相同的超类型。
2. 可以用一个或多个装饰者包装一个对象。
3. 装饰者可以在所委托被装饰者的行为之前或之后,加上自己的行为,以达到特定的目的。
4. 对象可以在任何时候被装饰,所以可以在运行时动态的,不限量的用你喜欢的装饰者来装饰对象。
5. 装饰模式中使用继承的关键是想达到装饰者和被装饰对象的类型匹配,而不是获得其行为。
6. 装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型。在实际项目中可以根据需要为装饰者添加新的行为,做到“半透明”装饰者。
适用场景与优缺点:
在以下情况下应当使用装饰模式:
1.需要扩展一个类的功能,或给一个类增加附加责任。
2.需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
3.需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变得不现实。
优点:
1. Decorator模式与继承关系的目的都是要扩展对象的功能,但是Decorator可以提供比继承更多的灵活性。
2. 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。
缺点:
1. 这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性。
2. 装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。
3. 装饰模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,就应该重新思考你的应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公开的行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择。