分类: 项目管理
2008-06-17 13:50:15
装饰模式,The
Decorator Pattern attaches additional
responsibilities to an object dynamically. Decorators provide a flexible
alternative to subclassing for extending functionality. 动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。
让我们来理解这句话。
我们系统中设计了“门”这个类。
现在,在系统的一个地方需要一个能够报警的Door,你来怎么做呢?你或许写一个Door的子类AlarmDoor,在里面添加一个子类独有的方法alarm()。嗯,那在使用警报门的地方你必须让客户(使用者)知道使用的是警报门,不然无法使用这个独有的方法。而且,这个还违反了Liskov替换原则。
也许你要说,那就把这个方法添加到Door里面,这样不就统一了?但是这样所有的门都必须有警报,至少是个“哑巴”警报。而当你的系统仅仅在一两个地方使用了警报门,这明显是不合理的——虽然可以使用缺省适配器来弥补一下。
这时候,你可以考虑采用装饰模式来给门动态的添加些额外的功能。
下面我们来看看装饰模式的组成,不要急着去解决上面的问题,到了下面自然就明白了!
Component
原有类的接口
ConcreteComponent
功能实现类。Component的具体实现类
Decorator
装 饰抽象类。与ConcreteComponent一样都继承了Component接口,但实现的方式与ConcreteComponent有区别。 ConcreteComponent通过单纯继承的方式来实现;而Decorator则通过对Component对象的封装与动作委让方式来实现。
下面是一个典型的Decorator的代码为:
清单1
public class Decorator implement Component {
Component component;
public Decorator(Component component) {
this.component = component;
}
public void doSomething() {
...
this.component.doSomething();
...
}
}
ConcreteDecoratorA 与 ConcreteDecoratorB
具体的装饰类。Decorator的具体实现类
下面是类图
下面看一个更具体的例子:
对于一家咖啡店来说,都有那些变化点呢?调味品的品种和价格会变吗?咖啡的品种和价格会变吗?咖啡和调味品的组合方式会变吗?YES! 对于一家咖啡店来说,这些方面肯定会经常发生改变的!那么,当这些改变发生的时候,我们的记账系统要如何应对呢? 如果调味品发生改变,那么我们只能从代码的层次重新调整Beverage基类,这太糟糕了;如果咖啡发生改变,我们可以增加或删除一个子类即可,这个似乎还可以忍受;那么咖啡和调味品的组合方式发生改变呢?如果顾客点了一杯纯黑咖啡外加两份砂糖和一份巧克力,或者顾客点了一杯脱咖啡因咖啡(Decaf)外加三份炼乳和一份砂糖呢?
那么回到装饰者模式,跟前面介绍过的模式一样,装饰者同样是一个很简单的模式,特别是画出类图和顺序图之后,一切都很清楚明了。这里只有一个地方需要特殊强调一下:Decorator是装饰者模式里非常特殊的一个类,它既继承于Component【IS A关系】,又维护一个指向Component实例的引用【HAS A关系】,换个角度来说,Decorator跟Component之间,既有动态组合关系又有静态继承关系,WHY?
这里为什么要这么来设计?上面我们说过,组合的好处是可以在运行时给对象增加职责,Decorator【HAS A】Component的目的是让ConcreteDecorator可以在运行时动态给ConcreteComponent增加职责,这一点相对来说还比较好理解;那么Decorator继承于Component的目的是什么?在这里,继承的目的只有一个,那就是可以统一装饰者和被装饰者的接口,换个角度来说,不管是ConcretComponent还是ConcreteDecorator,它们都是 Component,用户代码可以把它们统一看作Component来处理,这样带来的更深一层的好处就是,装饰者对象对被装饰者对象的功能职责扩展对用户代码来说是完全透明的,因为用户代码引用的都是Component,所以就不会因为被装饰者对象在被装饰后,引用它的用户代码发生错误,实际上不会有任何影响,因为装饰前后,用户代码引用的都是Component类型的对象,这真是太完美了!装饰者模式通过继承实现统一了装饰者和被装饰者的接口,通过组合获得了在运行时动态扩展被装饰者对象的能力。
我们再举个生活中的例子,俗话说“人在衣着马在鞍”,把这就话用装饰者模式的语境翻译一下,“人通过漂亮的衣服装饰后,男人变帅了,女人变漂亮了;”。对应上面的类图,这里人对应于ConcreteComponent,而漂亮衣服则对应于ConcreteDecorator;换个角度来说,人和漂亮衣服组合在一起【HAS A】,有了帅哥或美女,但是他们还是人【IS A】,还要做人该做的事情,但是可能会对异性更有吸引力了(扩展功能)!
从装饰者模式的角度来考虑问题,咖啡和调味品的关系应该 是:咖啡是被装饰对象而调味品是装饰者,咖啡和调味品可以任意组合,但是不管怎么组合,咖啡还是咖啡!原来这么简单啊!
具体看下面的类图:
如图所示,Beverage还是所有饮料的基类,它对应于装饰者模式类图里的Component,是所有被装饰对象的基类;HouseBlend, DarkRoast, Espresso, Decaf是具体的饮料(咖啡)种类,对应于前面的ConcreteComponent,即是具体的被装饰对象;CondimentDecorator对应于前面的Decorator,是装饰者的抽象类;而Milk,Mocha,Soy,Whip则都是具体的调味品,对于前面的ConcreteDecorator,也就是具体的装饰者。下面我们通过具体的代码再进一步理解一下基于装饰者模式的记账系统的实现。
Beverage还是所有饮料的基类,它对应于装饰者模式类图里的Component,是所有被装饰对象的基类
|
|
|
|
|
|
|
|
参考:
http://blog.csdn.net/ai92/archive/2005/03/01/307046.aspx
http://www.cnblogs.com/justinw/archive/2007/06/11/779356.html