译-设计模式-结构模式之Decorator

来源:互联网 发布:java object finalize 编辑:程序博客网 时间:2024/05/19 23:16

翻译整理自:https://refactoring.guru/design-patterns/decorator

更多请移步: 我的博客

目的

Decorator(装饰器)是一个结构设计模式,可以让你在封装包涵对象原有行为的基础上增加新的行为。

问题

你需要动态的添加或者移除一个对象的责任,但是你要做到和应用中其他代码的兼容。

当你需要扩展一个类的行为时继承时第一个想到的处理方式。然而,继承是静态的。你不能够增加一个新的类到程序中当它已经编译或者执行完成。

解决办法

装饰器模式依赖一个叫做装饰者(或者包装者)的特别类。他们和被封装的类拥有一样的接口,所以客户端代码不会注意到你用封装者替换了源对象。

所有的封装者这都持有一个源对象实例的强引用。大多数包装器使用传入其构造函数的对象初始化该字段。

所以,该如何动态改变他的行为呢?正如我提到的,封装者何和目标对象拥有一样的接口。当你调用装饰者的方法时,他执行被封装对象中同样方法并且在返回的结果中添加一些东西。它也可以在原始方法之前调用,但这取决于业务逻辑。

这里是有趣的一部分:你可以使用装饰者封装一个对象,然后再使用另外一个装饰器封装这个包装结果,等等。最终的行为结果是所有装饰器和源对象组合得到的。

现实世界的类比

穿衣服就是使用装饰者的例子。当你冷的时候,你用毛衣包裹自己。如果你还是冷,你可以在外边套一个夹克。如果下雨了,你还可以再传一件雨衣。

所有的服装“扩展”自你基本的行为,但不是你的一部分。因此,在你不需要他们的时候,可以轻松的移除它们。

结构

Structure

  1. Component为封装者何被封装者声明了一个通用的接口。

  2. Concrete Component是一个包涵基本行为并可以被装饰器修改的类。

  3. Base Decorator包涵一个被封装对象的强引用域。这个域应该被声明为Component类型,以便支持Concrete Components 和 Decorators.Base Decorator将所有操作委托给被封装对象。

  4. Concrete Decorators包涵可以被动态添加的额外行为。装饰器可以在调用被封装对象方法前后执行自己的行为。

伪代码

在这个例子中,装饰器通过加密保护金融数据,对已经存在的代码来讲是透明的。应用对金融数据做了加密和压缩装饰,当我们从硬盘读取数据时返回的是普通的数据,但是当我们写会到磁盘时数据被加密和压缩。

装饰者和金融类都有一个相同的接口,使得它们对客户端来讲是通用的。

// Common interface for all components.interface DataSource is    method writeData(data)    method readData():data// One of the concrete components can act as a base layer.class FileDataSource implements DataSource is    constructor FileDataSource(filename) { ... }    method writeData(data) is        Write data to file.    method readData():data is        Read data from file.// All other concrete components may act as wrappers.class DataSourceDecorator implements DataSource is    protected field wrappee: DataSource    constructor DataEncyptionDecorator(source: DataSource) is        wrappee = source    method writeData(data) is        wrappee.writeData(data)    method readData():data is        return wrappee.readData()// Concrete Decorators extend the functionality of a component they wrap.class EncyptionDecorator extends DataSourceDecorator is    method writeData(data) is        Encrypt passed data.        Pass the compressed data to wrappee's writeData() method.    method readData():data is        Get the data from wrappee's readData() method.        Decrypt and return that data.// You can wrap objects in several layers of decorators.class CompressionDecorator extends DataSourceDecorator is    method writeData(data) is        Compress passed data        Pass the compressed data to wrappee's writeData() method.    method readData():data is        Get the data from wrappee's readData() method.        Uncompress and return that data.// Option 1. A simple example of decorator assembly.class Application is    method dumbUsageExample() is        source = new FileDataSource('somefile.dat')        source.writeData(salaryRecords)        // a file with plain data        source = new CompressionDecorator(source)        source.writeData(salaryRecords)        // compressed file        source = new EncyptionDecorator(source)        source.writeData(salaryRecords)        // compressed and encrypted file// Option 2. Client code, which uses an external data source. SalaryManager// neither knows not cares about data storage specifics. It receives already// configured data source.class SalaryManager is    field source: DataSource    constructor SalaryManager(source: DataSource) { ... }    method load() is        return source.readData()    method save() is        source.writeData(salaryRecords)    // ...Other useful methods...// Application can assemble objects with a different set of functionality using// the same decorators at run time, depending on the configuration// or environment.class ApplicationConfigurator is    method configurationExample() is        source = new FileDataSource("salary.dat");        if (enabledEncryption)            source = new EncyptionDecorator(source)        if (enabledCompression)            source = new CompressionDecorator(source)        logger = new SalaryLogger(source)        salary = logger.load();    // ...Rest of an application code.

适用性

  • 当你需要动态赋予某个对象行为并且不需要破坏这个对象的代码。
    装饰器模式允许给某个对象动态的赋予新的行为,而且对客户端代码是隐式的。对象可以同时封装多个wrapper(译者注:就像同时穿了背心、衬衣和西装),结果是所有封装的堆叠结果。

  • 当不可能活着不合适通过继承来扩展对象的行为。
    许多编程语言都有final关键字来阻止未来对一个类的扩展。当处理这些代码,进行扩展的唯一选项就是适用装饰器模式。

如何实现

  1. 确保您的任务可以表示为一个主要组件和几个可选扩展。

  2. 创建Component(组件)接口,它需要描述该组件所有可被扩展的方法。

  3. 创建Concrete Component(具体组件)类并且实现业务逻辑。

  4. 创建Base Decorator(基础装饰)类。创建一个域来保存被封装对象的强引用。该域应该是Component类型,这样强引用就可以持有组件类和装饰器的强引用(译者注:即变量声明为接口类型,这样可以持有所有Component的所有子类)。

  5. 确保所有子类实现了Component接口。

  6. 确保Base Decorator类的所有方法都将方法执行委托给了被包装对象。它将允许Concrete Decorators(具体装饰器)仅扩展一部分组件行为,并且不需要修改其他行为。

  7. 创建Concrete Decorators类,该类从Base Decorator扩展。

  8. 一个Concrete Decorator可以在调用被封装对象相同方法前后执行它自己的的行为(你可以仅仅只调用弗雷德方法,因为它将最终调用封装方法)。

  9. Client代码必须负责配置包装层。Client应该通过Component的接口和其他类一起工作,使装饰器可以互换。

不必完全拘泥于以上步骤,一些情况下译者认为完全可以省略掉Base Decorator。

优缺点

  • 优点

    1. 比继承灵活
    2. 允许在运行时添加和删除行为
    3. 通过使用多层封住,组合几个额外的行为。
    4. 可以组合多个单行为实现使其实现更加复杂的行为。
  • 缺点

    1. 配置一个多封装对象是困难的。
    2. 导致很多小类。

和其他模式的关系

  • 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,它接受与当前类相同的类或接口的对象。

原创粉丝点击