设计模式拾荒之装饰器模式: 代理模式的双胞胎兄弟

  1. 参考书籍: 《Design Patterns: Elements of Reusable Object-Oriented Software》

设计模式用前须知

  • 设计模式种一句出现频率非常高的话是,“ 在不改动。。。。的情况下, 实现。。。。的扩展“ 。
  • 对于设计模式的学习者来说,充分思考这句话其实非常重要, 因为这句往往只对框架/ 工具包的设计才有真正的意义。因为框架和工具包存在的意义,就是为了让其他的程序员予以利用, 进行功能的扩展,而这种功能的扩展必须以不需要改动框架和工具包中代码为前提
  • 对于应用程序的编写者, 从理论上来说, 所有的应用层级代码至少都是处于可编辑范围内的, 如果不细加考量, 就盲目使用较为复杂的设计模式, 反而会得不偿失, 毕竟灵活性的获得, 也是有代价的。

装饰器模式 ( Decorator )

  • 设计意图

    • GOF: 将一些职责动态地添加到一个对象上。 装饰器模式提供了一种不创建新的子类, 而灵活扩展对象功能的解决方案。
    • 关键词: “动态添加职责” 、 “不创建新的子类”
  • GOF举例:

    • 有些时候我们希望给单个的对象增加一些功能或职责, 而不是给整个类添加职责。 例如对于一个图形界面工具包而言, 应该允许用户向各种界面组件添加边框和滚动的功能。
    • 实现这个目标的一种方案是利用继承。
      • 当一个类需要增加边框时, 就继承Border 类。 但是这样是很不灵活的, 因为哪些类能有边框被静态地(编译时刻)固定好了, 用户没有办法控制什么时候一个组件可以装饰一个边框。
    • 更加灵活的一种方案是装饰器模式
      • 把需要增加边框的类封装到另一个负责添加边框的对象中。 这个起封装作用的对象被叫做装饰器。
      • 为了使得装饰器对于用户代码透明, 装饰器需要符合被装饰对象的接口。 装饰器会将发送给 被装饰对象 的请求转发给 被装饰对象, 在此基础上, 可能会进行一些额外的操作( 如在转发前或转发后添加边框) 。 这种透明性可以允许你不断递归地嵌套装饰器, 从而添加无上限个数的功能。
    • 以上图为例, 假设我们拥有一个文本视图, 该视图在一个窗口内显示文本。 文本视图默认没有滚动条, 因为我们不一定总是需要滚动条。 当我们需要滚动条时, 我们可以用一个 ScrollDecorator 去添加它。
    • 假设我们还需要在文本框外侧添加一个粗黑的边框, 我们可以使用 BorderDecorator 去添加它。
    • 我们可以简单地把各种装饰器与 TextView 组合来产生所期望的结果。

  • 图例说明

    • 图片中的空心三角箭头,代表着继承(extends)或实现(Implement)关系, 由继承者/实现者 指向 被继承者/被继承者。
    • 图片中的实心三角箭头且箭头末尾没有圆圈的, 代表着单一的引用关系, 但是被引用的对象也有可能被其他对象引用。
    • 图片中的实心三角箭头且箭头末尾有圆圈的, 代表着一对多的引用关系。
    • 图片中的末端有圆圈的虚线是一个对方法体内容用伪代码说明的关系
  • 图例分析

    • VisualComponent 是可显示的组件对象的抽象类。 它定义了组件的绘制和事件响应操作(图例中没有画事件响应相关的操作)
    • 注意观察图例中的 BoderDecorator , 可以看出来, 装饰器是如何转发请求给被装饰对象的:
      • BorderDecorator 中的 draw() 操作会先直接调用Decorator 的 draw() 方法, 再调用自定义的 drawBorder( )。 而 Decorator 的 draw() 方法则又是直接调用被装饰的 Component 的 draw 方法
    • Decorator 的子类可以自由地为特定的功能添加操作。

装饰器模式与组合模式对比

  • 如果仔细观察 【装饰器模式】 的结构和 【组合模式】 的结构, 会发现他们非常相似

    • 装饰器模式
    • 组合模式
  • 事实是他们的确非常相似

    • 一个 装饰器 (Decorator) 可以被看做是一个退化的, 仅仅拥有一个组件的 复合实体(Composite)(但如此看待, 便会误解装饰器模式的核心点)。
    • 装饰器模式和组合模式都以递归的方式组合数量不限的对象。
  • 然而, 他们之间的区别也很明确。

    • 装饰器会增加一些附加的功能
      • 图例中, ConcreteDecorator 中的的operation 在调用component 的 operation() 之后调用了addedBehavior()
    • 而组合模式中的对象聚合并不会增加额外的功能。
      • 图例中, Composite 的 operation 方法中只是循环调用了所有 children 的 operation 方法 。
  • 从根本上来说, 组合模式和装饰器模式最大的区别就是其设计意图的区别。

    • 装饰器模式的设计意图是了在不添加子类的前提下, 为一个对象增添功能或职责。
    • 组合模式则专注于把相关的类组织起来, 使得编程者可以用相同的方式去操作组装出来的复合对象和未被组装的单体对象。
  • 组合模式和装饰器模式的设计意图虽然不同, 但却是互补的。 这使得他们往往被搭配在一起使用。 呈现如下的结构

  • 注意点
    • Decorator 都继承(或者实现)相同的抽象类(或接口) Component
    • Leaf 负责实现一些系统中最基础的类, 例如图形应用中的 Line, Circle, Rectangle
    • 从装饰器模式的角度看, 此时的一个复合实体 Composite 是一个可以被装饰的 ConcreteComponent.
    • 从组合模式的角度看, 此时的一个装饰器 Decorator , 是一个可以被组装的单体对象 LeafComponent

装饰器模式与代理模式对比

  • 如果说装饰器模式和组合模式还只是结构相似的话, 那代理模式和装饰器模式的结构几乎都是一模一样了

  • 代理模式

  • 装饰器模式

  • 可以看到, 如果从结构或者实现的角度来讲, 代理模式和装饰器模式完全一样, 都会持有一个对被代理(被装饰)对象的引用, 然后转发请求给被持有的对象。

  • 那么, 装饰器模式和代理模式如何区分

    • 【使用目的】 区分
      • 代理模式的目的在于进行 访问控制, 其意图在于为被代理对象提供一个替身, 而之所以提供替身而不直接访问的目的是, 原有的被代理对象可能不便于被直接访问(是一个远程对象), 或者是一个访问权限需要被限制的对象。
      • 装饰器模式的目的在于进行功能添加 / 功能增强 , 其意图在于为被装饰对象动态地增加功能或属性。
    • 【使用方式】 区分
      • 装饰器模式中的装饰器 Decorator 支持迭代式嵌套
      • 代理模式中的代理 Proxy 从设计逻辑上来讲, 是不应该 被迭代式嵌套的

你可能感兴趣的:(设计模式)