1、 Decorator模式简介
《设计模式》一书中对Decorator模式的意图是这样叙述的:
动态的给一个对象添加一些额外的职责。就增加功能来说,Decorator模式比生成子类更为灵活。
Decorator模式的工作原理是:可以创建始于Decorator对象(负责新功能的对象)终于原对象的一个对象“链”。实现方法:将原对象作为新对象的成员。
如上图中Decorator模式的类图隐含了一条对象链。每条链都始于一个Component对象(ConcreteComponent或Decorator)。每个Decorator对象后面都跟着另一个Decorator对象或原ConcreteComponent对象。对象链总是终于一个ConcreteComponent对象。
例如,在上图中,ConcreteDecoratorB对象执行其Operation方法,然后调用Decorator类的Operation方法。这又将调用ConcreteDecoratorB对象之后的Component对象的Operation方法。
上图为设置表头和页脚的Decorator模式类图,如果需要这样的一张票据:
Header1
Header2
Sales Ticket
Footer1
则实现代码:
Component * pComponent = new Header1(newHeader2(new Foorer1(new SalesTicket())));
pComponent->prtTicket();
2、 Decorator模式关键特征
意图 |
动态地给一个对象添加职责 |
问题 |
要使用的对象将执行所需基本功能。但是,可能需要为这个对象将添加某些功能,这些附加功能可能发生在对象的基础功能之前或之后。 |
解决方案 |
可以无需创建子类,而扩展一个对象的功能 |
参与者与协作者 |
ConcreteComponent让Decorator对象为自己添加功能。有时候用ConcreteComponent的派生类提供核心功能,在这种情况下ConcreteComponent类就不再是具体的,而是抽象的。Component类定义了所有这些类所使用的接口。 |
效果 |
所添加的功能放在小对象中。好处是可以在ConcreteComponent对象功能之前或之后动态添加功能。注意,虽然装饰对象可以在被装饰对象之前或之后添加功能,但对象链总是终于ConcreteComponent对象。 |
实现 |
创建一个抽象类来表示原类和要添加到这个类的功能。在装饰类中,将对新功能的调用放在对紧随其后对象的调用之前或之后,以获得正确的顺序。 |
3、 Decorator模式的本质
Decorator模式的使用场合是:各种可选功能在另一个肯定要执行的功能之前或之后执行。
上述Decorator模式的实现实际上是一种很糟糕的设计。例如,假设这些“装饰对象”(即可选功能)是由不同的开发组开发的,而且系统可能抛出一些异常,需要由每个装饰对象进行处理。理想情况下,可以相信这些开发组都能够按预期地实现—正确地捕获这些异常。然而,如果他们没有做到会怎么样呢?如果一个异常抛出了,而某个开发组编写的代码无法捕获它,整个系统这时可能crash。
另一种方案是让客户对象捕获异常。但是,这也就丧失了Decorator模式的价值,因为客户类需要完成比它以前更多的任务。
更健全的解决方案是实现一个与ConcreteComponent对象接口相同的对象集合。它调用装饰对象,捕获后者没有捕获的任何必须捕获的异常。事实上,这可能具有附加的好处:装饰对象再也不必要与ConcreteComponent对象接口相同了。
关键在于,Decorator模式有如下约束因素:
- 存在几种可选功能;
- 这些装饰对像可能遵循也可能不遵循所有规则;
- 需要某种方式以所需的不同顺序调用这些装饰对象,但是又不能加重客户对象的负担;
- 不希望应用程序必须承担知道使用哪些装饰对象(甚或是否存在)的职责。
这样思考Decorator模式将使模式的意图和实现分离开来。