装饰者模式(Decorator Pattern)
*利用组合(composition)和委托(delegation)可以在运行时实现继承行为的效果,动态地给对象加上新的行为。
*利用继承扩展子类的行为,是在编译时静态决定的;利用组合的做法,可以在运行时动态地扩展对象的行为。
软件设计原则:类应该对扩展开放,对修改关闭。这就是我们常说的开放关闭原则。
*开放-关闭原则使类容易扩展,在不修改代码的情况下,通过搭配实现新的行为。这样的设计可以应对改变,比如增加新功能或需求发生变更。
OO设计技巧:允许系统在不修改代码的情况下,进行功能扩展。
装饰者模式:动态地将责任加到对象身上。如果要扩展功能,装饰者模式提供了比继承更有弹性的替代方案。
装饰者模式中,装饰者可以在被装饰者的行为之前或之后,加上自己的行为,以实现特性的目的。
装饰者模式的几个缺点:
(1)有时在设计中加入大量的小类,变得不容易理解。
(2)有的客户端代码依赖于特定的类型(这是个比较糟糕的习惯,违反了“针对接口编程,而不是针对实现编程”的设计原则),当服务器端引入装饰者模式时,客户端就会出现状况。
(3)装饰者模式使得实例化组件的复杂度提升。
*遵循开放-关闭原则设计系统,努力使关闭的部分(不变)和开放的部分(变化)隔离开来。
问题背景:
设计一个咖啡订单系统。咖啡可以加配料,不同的配料收费不同。如果一个顾客想要摩卡(Mocha)和奶泡(Whip)深焙咖啡(DarkRoast)该怎样去计算费用呢?
类结构图
Beverage(饮料)是一个抽象类,店内所提供的饮料都必须继承此类。cost()方法是抽象的,子类必须定义自己的实现。description(叙述)的实例变量,由每个子类设置,用来描述饮料,如“超优深焙咖啡豆”。利用getDescription()方法返回此叙述。购买咖啡时,可以要求在其中加入各种调料,如:牛奶、豆浆、摩卡等。咖啡馆会根据所加入的调料收取不同的费用。如果直接用继承,会造成类爆炸。如果从基类Beverage下手,加上实例变量代表是否加上调料(牛奶、豆浆、摩卡……),看起来是不错,但是不符合我们的设计原则。如调料价钱改变会使我们更改基类代码、一旦出现新的调料,我们就需要加上新方法,并改变超类中的cost()等,所以我们使用到了装饰者模式。
先从Beverage类下手
1 2 3 4 5 6 7 8 9 |
|
实现调料(Condiment)类,同时这个类也是个装饰者类
//必须让CondimentDecorator 能够取代Beverage public abstract class CondimentDecorator extends Beverage{ //所有转饰者必须重新实现getDescription public abstract String getDescription(); }基类已经创建好了,让我们来实现那些饮料(Beverage)类
//让Espresso扩展自Beverage,因为Espresso是一种饮料
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
我们已经完成了抽象组件(Beverage),有了具体组件(HouseBlend),也有了抽象装饰者(CondimentDecorator)。现在,我们就来实现具体装饰者:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
测试代码:
1 2 3 4 5 6 7 8 9 10 11 |
|
输出为:
Espresso $1.99
HouseBlend,Mocha $1.09
总结
装饰者模式:动态地将责任附件到对象上。若要扩展功能,装饰者提东了比继承更有弹性的替代方案。
*装饰者和被装饰对象有相同的超类型
*你可以用一个或者多个装饰者包装一个对象。
*既然装饰者和被装饰对象有相同的超类型,所以在任何需要原始对象(被包装的)的场合,可以用装饰过的对象代替它。
*装饰者可以在所委托被装饰者的行为前与/或之后,加上自己的行为,已达到特定的目的。
*对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰对象
设计原则:类应该对扩展开放,对修改关闭(开放-关闭原则)。
注意:遵循开放-关闭原则,通常会引入新的抽象层次,增加代码的复杂度。你需要把注意力集中在设计中最有可能改变的地方,然后应用开放-关闭原则。在选择需要被扩展的代码部分时要小心,每个地方都采用开放-关闭原则,是一种浪费,也没必要,还会导致代码变得复杂且难以理解。
要点:
-
- 继承属于扩展形式之一,但不见得是达到弹性设计的最佳方式。
- 在我们的设计中,应该允许行为可以被扩展,而无须修改现有的代码。
- 组合和委托可用于在运行时动态地加上新的行为。
- 除了继承,装饰者模式也可以让我们扩展行为,
- 装饰者模式意味着一群装饰者类,这些类用了包装具体组件。
- 装饰者类反映出被装饰的组件类型(事实上,他们具有相同的类型,都经过接口或继承实现)。
- 装饰者可以在被装饰者的行为前面与/或后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的。
- 你可以用无数个装饰者包装一个组件。
- 装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型。
- 装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂。