一、意图
适配器模式的意图是:动态地将额外的责任附加到对象上。装饰器提供了一个灵活的子类扩展功能的替代方法。
模式解决了这样的问题。
1、如何将职责动态地添加到对象?
2、如何在运行时扩展对象的功能?
“职责表示对象提供的某种行为的义务。”职责,行为和功能的这两种术语通常可以互换。
有时我们想向单个对象而不是整个类添加责任。例如,图形用户界面工具箱应允许添加边框等属性或滚动条到任何用户界面组件的行为。
例如,可重用的GUI/Web对象(如按钮,菜单或树小部件)。应该可以在运行时动态地将修饰(即边框,滚动条等)添加到基本GUI/Web对象。 在装饰器模式中,修饰是指对对象增加责任的任何事物。【GoF,p47】
二、问题
子类化是编译时静态扩展功能(向类添加职责)的标准方法。实例化子类(Subclass1)后,该功能将在其生命周期内绑定到该实例,并且无法在运行时进行修改。
如果我们想在运行时扩展对象的功能,而不是在编译时扩展类的功能,则可以避免这种方法。
例如,I/O数据流对象【java平台】。应该可以向仅处理原始二进制数据的基本I/O对象添加诸如处理数据类型和缓冲数据之类的职责。
例如,集合【java Collections Framework】。应该可以向集合中添加自动同步(线程安全)或取消修改集合功能。
三、解决方案
装饰器模式提供了一个解决方案
- 定义单独的对象,这些对象将责任添加到对象中。
- 在运行时遍历对象以扩展对象的功能。
- 此模式的关键思想是遍历Decorator“装饰器”(已添加)的对象(向其添加职责)的单独对象。装饰器Component透明地实现该接口,以便他可以充当要装饰的组件的透明外壳。“客户端通常无法告诉他们是在处理组件还是其附件。【GoF,p44】”
- 定义单独的 Decorator 对象
定义一个类(Decorator),该类维护对Component对象(component)的引用,并将请求转发到该组件(component.operation())。
定义Decorator1实现额外的功能(addbehavior()的子类),以在转发请求之前和/或之后执行
因为装饰器是装饰组件的透明外壳,所以他们可以递归嵌套以增加职责的开放性。
更改装饰器的顺序允许添加职责的任意组合。
例如,在上面序列图中,客户端通过两个嵌套的装饰器对象进行工作,这些装饰器对象向Component1对象添加了责任(转发请求之后)。
四、动机
-
问题:
在编译时扩展功能
子类的爆炸式增长 -
解决方案
在运行时拓展功能
递归嵌套的装饰器
五、适用
设计问题
在运行时扩展功能
- 如何动态地将职责添加到对象(或从对象撤回)?
- 如何在运行时扩展对象的功能?
- 如何定义在运行时扩展的简单类?
- 而不是在复杂类中实现所有可预见的功能?
子类的灵活替代
- 如何为子类提供灵活的替代方案,以在编译时扩展类的功能?
重构问题
僵化的代码
- 如何重构包含硬连线扩展(编译时实现依赖项)的类?将装饰物移至装饰器?
六、结构
-
Client
引用Component接口 -
Component
为component1和Decorator对象定义通用接口 -
component1
定义要修饰的对象 -
Decorator
维护对Component对象(component)的引用。将请求转发到此组件(component.operation())。 -
Decorator1,DDecorator2,…
实现在转发请求前和/或之后执行的附加功能(addBehavior())。
七、协作
- 在此示例场景中,一个Client对象通过两个装饰器工作,这两个装饰器向Component1对象添加了责任。
- Client调用Decorator1对象上的operation()。
- Decorator1将请求转发给Decorator2对象
- Decorator2将请求转发给Component1对象
- Component1执行请求并返回Decorator2对象
- Decorator2执行其他功能(通过调用自身的addBehavior())并返回Decorator1
- Decorator1执行其他功能并返回Client。
八、结果
优点
*为子类提供灵活的替代方法。*装饰器为通过子类扩展功能提供灵活的替代方案。通过与不同的装饰器协作,可以很容易地组合(混合、排序、复制等)功能。子类化将需要为每个新的功能组合创建一个新的子类。
*允许不限数量的附加功能。*因为装饰器是装饰对象的透明外壳,所以他们可以递归嵌套,这允许增加扩展功能的数量。客户不知道他们是直接使用对象还是通过其装饰器使用对象。
*简化类。*您可以定义一个简单的类,并使用Decorator对象逐步添加功能,而不必尝试在复杂的可定制类中支持所有可预见的功能。
缺点
不提供对象标识的可靠性 装饰器对象是透明的,但与装饰器对象不同。因此,依赖于对象标识的应用程序不应使用装饰器。
九、实施
接口的一致性
-
装饰器的关键是
(1)维护对装饰对象的引用(component)
(2)通过将所有的请求转发给装饰对象(component。operation())来透明地实现装饰对象 的接口。 -
被称为 透明外壳 ,客户端通常不知道他们是在处理组件还是他的外壳。
十、代码
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
Component component=new Component1();
System.out.println("(1) "+component.operation());
component=new Decorator1(new Decorator2(component));
System.out.println("(2) "+component.operation());
}
}
abstract class Component{
public abstract String operation();
}
class Component1 extends Component{
@Override
public String operation() {
// TODO Auto-generated method stub
return "hello world from Component1";
}
}
abstract class Decorator extends Component{
Component component;
public Decorator(Component component) {
this.component=component;
}
public String operation() {
// TODO Auto-generated method stub
return component.operation();
}
}
class Decorator1 extends Decorator{
public Decorator1(Component component) {
super(component);
// TODO Auto-generated constructor stub
}
public String operation() {
// TODO Auto-generated method stub
String result=super.operation();
return addBehavior(result);
}
private String addBehavior(String result) {
return "***"+result+"***";
}
}
class Decorator2 extends Decorator{
public Decorator2(Component component) {
super(component);
// TODO Auto-generated constructor stub
}
public String operation() {
// TODO Auto-generated method stub
String result=super.operation();
return addBehavior(result);
}
private String addBehavior(String result) {
return "==="+result+"===";
}
}
十一、相关模式
门面模式