概述
装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。即组合的方案。
使用装饰者,你将能够在不修改任何底层代码的情况下,给对象赋予新的职责。
咖啡店实例
以咖啡店为实例,讲解装饰者模式的使用。
一家咖啡店的扩张速度很快,他们准备更新订单系统,以符合他们的饮料供应要求。
他们原先的设计是这样的,Beverage(饮料)是一个抽象类,店内所提供的饮料都必须继承此类。
该类主要有两个方法:
-
getDescription()获取描述的方法,由每个子类设置,用来描述饮料,例如“超优深培(Dark Roast)”。
-
cost()计算价格的抽象方法,子类必须定义自己的实现。
购买咖啡时,也可以要求在其中加入各种调料,例如:蒸奶(Steamed Milk)、豆浆(Soy)、摩卡(Mocha,也就是巧克力风味)或覆盖奶泡。咖啡店会根据所加入的调料收取不同的费用。所以订单系统必须考虑到这些调料部分。
一个错误的示范
将各种调料和咖啡的每种组合,都设为一个类。这不仅造成类的爆炸,并且每个cost()方法都需单独算出咖啡和订单上各种调料的价钱。
很明显,这个设计制造了一个维护噩梦。如果豆浆价钱上涨怎么办?新增一种焦糖调料风味时,怎么办?
一个改进方案
改进方案,利用实例变量和继承,追踪这些调料。
先从Beverage基类下手,加上实例变量代表是否加上调料(牛奶、豆浆、摩卡、奶泡…)。
Beverage类中的cost()不再是一个抽象方法,我们提供了cost()的实现,让它计算要加入各种饮料的调料价钱。子类将覆盖cost(),但是会调用超类的cost(),计算出基本饮料加上调料的价钱。
该改进方案存在的问题:
1、调料价格的改变会使我们更改现有代码
2、一旦出现新的调料,就需要加上新的方法,并改变超类中的cost()方法。
3、以后可能会开发出新饮料。对一些饮料而言,例如冰茶,某些调料可能并不适合,但是在这个设计方案中,子类仍将继承那些不适合的方法,例如加奶泡hasWhip()。
4、万一顾客想要双倍摩卡咖啡,该类很难实现。
既然存在以上问题,那如何改进呢,看如何用装饰者模式实现:《HeadFirst设计模式-装饰者模式(中)》