目的
Decorator(装饰器)是一个结构设计模式,可以让你在封装包涵对象原有行为的基础上增加新的行为。
问题
你需要动态的添加或者移除一个对象的责任,但是你要做到和应用中其他代码的兼容。
当你需要扩展一个类的行为时继承时第一个想到的处理方式。然而,继承是静态的。你不能够增加一个新的类到程序中当它已经编译或者执行完成。
解决办法
装饰器模式依赖一个叫做装饰者(或者包装者)的特别类。他们和被封装的类拥有一样的接口,所以客户端代码不会注意到你用封装者替换了源对象。
所有的封装者这都持有一个源对象实例的强引用。大多数包装器使用传入其构造函数的对象初始化该字段。
所以,该如何动态改变他的行为呢?正如我提到的,封装者何和目标对象拥有一样的接口。当你调用装饰者的方法时,他执行被封装对象中同样方法并且在返回的结果中添加一些东西。它也可以在原始方法之前调用,但这取决于业务逻辑。
这里是有趣的一部分:你可以使用装饰者封装一个对象,然后再使用另外一个装饰器封装这个包装结果,等等。最终的行为结果是所有装饰器和源对象组合得到的。
现实世界的类比
穿衣服就是使用装饰者的例子。当你冷的时候,你用毛衣包裹自己。如果你还是冷,你可以在外边套一个夹克。如果下雨了,你还可以再传一件雨衣。
所有的服装“扩展”自你基本的行为,但不是你的一部分。因此,在你不需要他们的时候,可以轻松的移除它们。
结构
Component为被封装者声明了一个通用的接口。
Concrete Component是一个包涵基本行为并可以被装饰器修改的类。
Base Decorator包涵一个被封装对象的强引用域。这个域应该被声明为Component类型,以便支持Concrete Components 和 Decorators.Base Decorator将所有操作委托给被封装对象。
Concrete Decorators包涵可以被动态添加的额外行为。装饰器可以在调用被封装对象方法前后执行自己的行为。
伪代码
在这个例子中,装饰器通过加密保护金融数据,对已经存在的代码来讲是透明的。应用对金融数据做了加密和压缩装饰,当我们从硬盘读取数据时返回的是普通的数据,但是当我们写会到磁盘时数据被加密和压缩。
装饰者和金融类都有一个相同的接口,使得它们对客户端来讲是通用的。
1 | // Common interface for all components. |
适用性
当你需要动态赋予某个对象行为并且不需要破坏这个对象的代码。
装饰器模式允许给某个对象动态的赋予新的行为,而且对客户端代码是隐式的。对象可以同时封装多个wrapper(译者注:就像同时穿了背心、衬衣和西装),结果是所有封装的堆叠结果。当不可能活着不合适通过继承来扩展对象的行为。
许多编程语言都有final
关键字来阻止未来对一个类的扩展。当处理这些代码,进行扩展的唯一选项就是适用装饰器模式。
如何实现
确保您的任务可以表示为一个主要组件和几个可选扩展。
创建Component(组件)接口,它需要描述该组件所有可被扩展的方法。
创建Concrete Component(具体组件)类并且实现业务逻辑。
创建Base Decorator(基础装饰)类。创建一个域来保存被封装对象的强引用。该域应该是Component类型,这样强引用就可以持有组件类和装饰器的强引用(译者注:即变量声明为接口类型,这样可以持有所有Component的所有子类)。
确保所有子类实现了Component接口。
确保Base Decorator类的所有方法都将方法执行委托给了被包装对象。它将允许Concrete Decorators(具体装饰器)仅扩展一部分组件行为,并且不需要修改其他行为。
创建Concrete Decorators类,该类从Base Decorator扩展。
一个Concrete Decorator可以在调用被封装对象相同方法前后执行它自己的的行为(你可以仅仅只调用弗雷德方法,因为它将最终调用封装方法)。
Client代码必须负责配置包装层。Client应该通过Component的接口和其他类一起工作,使装饰器可以互换。
不必完全拘泥于以上步骤,一些情况下译者认为完全可以省略掉Base Decorator。
优缺点
优点
- 比继承灵活
- 允许在运行时添加和删除行为
- 通过使用多层封住,组合几个额外的行为。
- 可以组合多个单行为实现使其实现更加复杂的行为。
缺点
- 配置一个多封装对象是困难的。
- 导致很多小类。
和其他模式的关系
Adapter提供不同的接口来达到目的。Proxy提供相同的接口。Decorator提供增强的接口。
Adapter意味着改变一个存在对象的接口。Decorator在不改变原有接口的情况下增强另外一个对象。Decorator对应用来讲比Adapter更加透明。因此,Decorator支持递归组合,这对于纯Adapter是不可能的。
Chain of Responsibility(责任链)和Decorator具有非常普通的类机构。它们都依赖于一系列对象的递归组合来执行。但是它们也有几个关键的不同点。
Chain of Responsibility的处理者可以执行随意的行为操作,处理者之间相互独立。它们可以随意的终端下一步的调用。另一方面,各种Decorator扩展一个特别的行为并且假设保持接口一致。并且,Decorator不允许随意中断执行链。
Composite和Decorator拥有类似的结构图,因为它们都依赖递归组合来组织一个对象开闭的数量。
装饰器可以看作只有一个组件的退化组合。然而,Decorator向对象增加了额外的责任,而Composite只是对其子类执行相同的行为的“summs up”。但是它们也可以协作:Composite可以使用Decorator来改变树组件的行为。
大量使用Composite和Decorator模式的设计通常可以从Prototype中受益。它允许克隆复杂的结构,而不是从头重新构建它们。
装饰器可以让您更改对象的皮肤。策略让你改变勇气。
Decorator和Proxy有相似的结构,但是目的不同。两种模式都建立在将工作委托给其他对象的组合原则上。然而,Proxy自己管理他持有服务对象的生命周期,而Decorator结构由客户端控制。
Java中模式的使用
用例:Decorator在Java中是十分标准的,尤其是和流相关的代码。
这有几个Java核心库中使用Decorator的例子:
java.io.InputStream,OutputStream,Reader和Writer的所有子类都有接受它们自己类型的构造方法。
java.util.Collections,方法checkedXXX(),synchronizedXXX()和unmodifiableXXX()。
javax.servlet.http.HttpServletRequestWrapper和HttpServletResponseWrapper。
鉴定:可以通过创建方法或构造函数来识别Decorator,它接受与当前类相同的类或接口的对象。