0%

装饰者模式(Decorator)

这是设计模式系列第三篇博文,装饰者模式,也是《Head First 设计模式》的学习笔记。

定义

装饰者模式 动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

问题

老套路,为了后面讨论的需要,先简单的复述下《Head First 设计模式》中的例子(简化版)。

  1. 星巴克中有一系列的咖啡(Coffee),其中一种为 DarkRoast。
  2. 咖啡可以配上不同的调料,假设有两种调料 Mocha 和 Whip。
  3. 顾客点咖啡之后,可以自主选择调料。

在这三个条件之下,要怎么来设计星巴克的点单系统,以计算出最后的价格?

可以把咖啡和调料都抽象成顾客需要消费的产品(Beverage),咖啡对象和调料对象都实现 Beverage 协议。

Decorato

每个 Beverage 对象的cost方法把自己的价格加上beverage成员的价格再返回即可。例如:

1
2
3
4
- (double)cost
{
return 1.1f + [self.beverage cost];
}

当用户点单 DarkRoast 并添加 Mocha 和 Whip 调料时,

1
2
3
id<Beverage> beverage = [DarkRoast new];
beverage = [[Mocha alloc] initWithBeverage:beverage];
beverage = [[Whip alloc] initWithBeverage:beverage];

最后在订单中保存beverage对象即可。

讨论

其实这种咖啡加调料的案例也可以用策略模式(Strategy Pattern)来做。

定义一个协议Condiment来表示调料,Coffee类表示咖啡。
Coffee中提供一个addCondiment的方法添加调料,提供cost方法返回价格。

用策略模式也可以实现这个需求,但是两者哪种更好或者说更优雅呢?
首先,两种方式都可以达到目的,那就主要比较下两者的扩展性。
假设后续需要增加一种包装,普通外带包装,干冰外带包装。那按照策略模式,其实还得再修改Coffee类,增加一个方法addPackage方法来解决这个场景。而按照装饰者模式,则只需要增加包装的装饰类即可。

策略模式和装饰者模式对比:

  1. 策略模式有明显的从属关系,例如策略模式(Strategy Pattern)中提到的DuckFlyBehavior从属于Duck,不可能存在Duck从属于FlyBehavior。而在装饰者模式者,每个装饰者都是平等的,A 可以装饰 B,B 也可以装饰 A。
  2. 策略模式中,主体和行为本质不是同一类别(DuckBehavior不同类别)。而装饰者模式模式中,每个装饰者都是同一类别,都有共同的属性或者行为(咖啡,调料,包装都属于顾客购买的产品)。

想明白这两个区别之后,再来决定是策略模式还是装饰者模式的时候就更清晰明了了。

设计原则

类应该对扩展开放,对修改关闭。

传送门

策略模式(Strategy Pattern)
观察者模式(Observer Pattern)