HEAD IN DESIGN PATTERNS读书笔记——DECORATOR 模式

上一篇 / 下一篇  2007-06-26 21:36:59 / 个人分类:软件设计

第三个PATTERN——DECORATOR

StarBuzz Coffee最近发展的非常迅速,他们决定更新他们的饮料订单系统。

他们开始提出的类图是这样的:

然而除了咖啡之外,你可能还会要一些调料,比如牛奶、豆汁、摩卡等等,StarBuzz Coffee会对这些收取一点费用。而订单系统需要根据没一种需求指定项目并计算总价。于是按照上面的方案最终导致了类的数量的爆炸。

也许你会说,为什么需要那么多的类,可以把调料作为成员变量加到基类中,子类继承得到这些调料的信息。那么试试看:

现在我们在Beverage类里实现cost()方法(而不是让它作为抽象方法),这样它可以计算一个具体的饮料实例的调料的花费。子类仍然会重写cost(),但它们依然会调用超类的版本,这样它们就可以计算基本的饮料加上调料的总花费。

但是有一系列因素的变化会影响这个设计:

调料价格的变化会迫使我们修改现有的代码

新的调料会迫使我们在超类中增加新方法以及修改cost()方法。

我们可能会有一些新的饮料类型,对于其中的一些饮料,可能使用调料是不恰当的,但是它们还是会继承hasWhip()这样的方法(就像第一章开始的例子中橡皮鸭继承fly()方法一样)。

如果客户要求双份的牛奶怎么办?

下面是我们改变的设计:我们将由一个饮料开始,在运行时用调料来“装饰”它。例如,客户需要一个有摩卡和蛋奶的Dark Roast,我们的步骤将是:

1.      得到DarkRoast对象

 

2.      Mocha对象来装饰它

Mocha对象是一个decorator,它的类型镜像了它所装饰的类型——Beverage(此处的镜像指它们是同一种类型)。因此,Mocha也有一个cost()方法。

3.      Whip对象来装饰它

4.      调用cost()方法,依靠委派来增加调料的花费

首先调用最外层Whip对象的cost()方法,它调用Mocha对象的cost()方法,Mocha对象调用DarkRoast对象的cost()方法。

DarkRoast对象返回它的花费,99美分,Mocha对象加上它的花费20美分,返回总值共1.19美元,Whip对象加上它的花费10美分,返回总值1.29美元

 

l       Decorator和被它装饰的对象有着同样的超类。

l       可以使用一个或多个decorator来包装一个对象

l       既然decorator和被它装饰的对象有着同样的超类,在使用一个原始(被包装的)对象的地方,我们可以传递一个装饰了的对象。

l       Decorator在把方法委派给它装饰的对象之前或者之后,把它自己的行为加上去来完成剩余的工作(这一点很关键!)

l       对象可以在任何时候被装饰,因此我们可以在运行时用任意多个decorator来装饰它。

 

现在我们来看DECORATOR PATTERN的描述:

      Decorator Pattern动态地为一个对象附加额外的职责。Decorators提供了区别于通过子类化来扩展功能的另一种解决方案。

      

 

现在我们的Starbuzz饮料系统变成下面的框架:

这里CondimentDecorator继承Beverage类是为了获得类型的匹配,而不是通过继承来获得行为(behavīor)。行为是通过其中包含的基本的component以及其他decorator的组合来获得的。

如果依靠继承,我们的行为只能在编译时静态地决定。通过composition,我们可以在运行时任意地混合使用decorators

Decorator模式的一个实例是Java中的I/O类。FileInputStream, BufferedInputStream, LineNumberInputStream——看着眼熟吧。



TAG: 软件设计

 

评分:0

我来说两句

Open Toolbar