这是设计模式系列第三篇博文,装饰者模式,也是《Head First 设计模式》的学习笔记。
定义
装饰者模式 动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
问题
老套路,为了后面讨论的需要,先简单的复述下《Head First 设计模式》中的例子(简化版)。
- 星巴克中有一系列的咖啡(Coffee),其中一种为 DarkRoast。
- 咖啡可以配上不同的调料,假设有两种调料 Mocha 和 Whip。
- 顾客点咖啡之后,可以自主选择调料。
在这三个条件之下,要怎么来设计星巴克的点单系统,以计算出最后的价格?
可以把咖啡和调料都抽象成顾客需要消费的产品(Beverage),咖啡对象和调料对象都实现 Beverage 协议。
每个 Beverage 对象的cost
方法把自己的价格加上beverage
成员的价格再返回即可。例如:
1 | - (double)cost |
当用户点单 DarkRoast 并添加 Mocha 和 Whip 调料时,
1 | id<Beverage> beverage = [DarkRoast new]; |
最后在订单中保存beverage
对象即可。
讨论
其实这种咖啡加调料的案例也可以用策略模式(Strategy Pattern)来做。
定义一个协议Condiment
来表示调料,Coffee
类表示咖啡。Coffee
中提供一个addCondiment
的方法添加调料,提供cost
方法返回价格。
用策略模式也可以实现这个需求,但是两者哪种更好或者说更优雅呢?
首先,两种方式都可以达到目的,那就主要比较下两者的扩展性。
假设后续需要增加一种包装,普通外带包装,干冰外带包装。那按照策略模式,其实还得再修改Coffee
类,增加一个方法addPackage
方法来解决这个场景。而按照装饰者模式,则只需要增加包装的装饰类即可。
策略模式和装饰者模式对比:
- 策略模式有明显的从属关系,例如策略模式(Strategy Pattern)中提到的
Duck
,FlyBehavior
从属于Duck
,不可能存在Duck
从属于FlyBehavior
。而在装饰者模式者,每个装饰者都是平等的,A 可以装饰 B,B 也可以装饰 A。 - 策略模式中,主体和行为本质不是同一类别(
Duck
和Behavior
不同类别)。而装饰者模式模式中,每个装饰者都是同一类别,都有共同的属性或者行为(咖啡,调料,包装都属于顾客购买的产品)。
想明白这两个区别之后,再来决定是策略模式还是装饰者模式的时候就更清晰明了了。
设计原则
类应该对扩展开放,对修改关闭。