装饰者模式
动态地将责任附加到对象上,若要拓展功能,装饰者提供了比继承更有弹性的替代方案。
设计原则
类应该对拓展开放,对修改关闭
问题描述:
星巴兹咖啡店扩张速度太快了,准备更新订单系统,以合乎他们的饮料供应要求。购买咖啡的时候,可以要求加入各种调料,例如:蒸奶,豆浆,摩卡等,星巴兹会根据所加入的调料收去不同的费用,所以订单系统必须考虑到这些调料部分。
解决方案:
一、对每一种组合定义一种类型,由于组合数目太多,会导致类爆炸
二、利用实例变量和继承可以追踪这些调料
这个设计面临的问题:
1.调料价格改变会使我们改变现有代码;
2.一旦出现新的调料,我们就需要加上新的方法,并且改变超类中的cost()方法
3.以后可能会开发出新的饮料,对这些饮料而言(例如:冰茶),某些调料可能不合适,但在这个设计中,Tea子类扔将继承那些不合适的方法,例如加奶泡
4.万一顾客想要双倍摩卡咖啡,怎么办?
三、装饰者模式
采用不一样的做法,我们要以饮料为主体,然后在运行时以调料来装饰饮料,比方说顾客想要摩卡和奶泡咖啡,那么要做的就是
1.拿一个深焙咖啡对象,
2.以摩卡对象装饰它
3.以奶泡对象装饰它
4.调用cost()方法,并依赖委托将调料的价钱加上去
运行结果:#include<iostream> #include<string> using namespace std; //饮料类是一个虚类,有两个方法 // 其中cost 必须在子类中实现 class Beverage{ public: Beverage(){ description = "Unknown Beverage" ; } virtual string getDescription(){ return description; } virtual double cost() = 0; string description; }; //实现一些饮料类,Esprsso 和 HouseBlend class Espresso:public Beverage{ public: Espresso(){ description = "Espresso"; } double cost(){ return 1.99; } }; class HouseBlend:public Beverage{ public: HouseBlend(){ description = "House Blend Coffee"; } double cost(){ return 0.89; } }; // 调料类,也就是装饰者类,必须让CondimentDecorator能够 //取代Beverage,所以将其从Beverage 继承 class CondimentDecorator: public Beverage{ virtual string getDescription() = 0; }; /* 要让Mocha能够引用一个Beverage,做法如下: (1)用一个实例变量记录饮料,也就是被装饰者 (2)想办法让被装饰者被记录到实例变量中,这里的做法是: 把饮料当做构造器的参数,再由构造器将此饮料记录在实例变量中 */ class Mocha : public CondimentDecorator{ public: Mocha(Beverage *beverage){ this->beverage = beverage; } string getDescription(){ return beverage->getDescription() + " , Mocha"; } double cost(){ return 0.20 + beverage->cost(); } private: Beverage *beverage; }; class Soy : public CondimentDecorator{ public: Soy(Beverage *beverage){ this->beverage = beverage; } string getDescription(){ return beverage->getDescription() + " , Soy"; } double cost(){ return 0.20 + beverage->cost(); } private: Beverage *beverage; }; int main(){ Beverage *beverage = new Espresso(); cout << beverage->getDescription() << " $" <<beverage->cost() << endl; /* Beverage beverage2 = new DarkRoast(); beverage2 = new Macha(beverage2); beverage2 = new Macha(beverage2); beverage2 = new Whip(beverage2); cout << beverage2.getDescription() << " $" << beverage.cost() << endl; */ Beverage *beverage3 = new HouseBlend(); beverage3 = new Soy(beverage3); //双份的Mocha beverage3 = new Mocha(beverage3); beverage3 = new Mocha(beverage3); cout << beverage3->getDescription() << " $" << beverage3->cost() << endl; delete beverage; delete beverage3; }
总结:
装饰者和被装饰对象有相同的基类型,这样才能一层层修饰下去
你可以用一个或者多个装饰包装一个对象
既然装饰者和被装饰对象有相同的超类型,所以在任何需要原始对象的场合,可以用装饰过的对象来代替它
装饰者可以在所委托被装饰者的行为之前或之后,加上自己的行为,以达到装饰的目的
但是装饰者会导致设计中出现很多小对象,如果过度使用,会让程序变得复杂