装饰者模式(Decorator Pattern)

Wafer Li ... 2017-03-20 《Head First 设计模式》笔记
  • DesignPattern
  • 读书笔记
大约 3 分钟

# 1. 概述

装饰者模式 动态的 将责任附加到对象上。

若要扩展功能,装饰者提供了比继承更有弹性的解决方案

# 2. 新的设计原则

类应当对扩展开放,对修改关闭

这乍看上去很矛盾,如何做到“既开放又关闭” 呢?

实际上,我们可以采用 组合委托 来达到扩展的目的;

避免因为扩展而需要修改代码

Bug 总是在修改、新增代码时引入的; 如果能够尽量减少对代码的反复更改,那么就可以更有效的减少和避免 Bug

装饰者模式就很好的体现了 “开放——关闭” 原则。

# 3. 原理

使用不同的 装饰者对象 来对 主体对象 进行装饰;

通过 委托 来进行组合工作。

这里有一个很重要的地方就是,为什么能实现上面图示的 包装方法委托

其使用到的技巧就是, 装饰者对象实际上也是主体对象,即它们有相同的超类。

如果不具备相同超类的话,最多只能做到一层包装,而无法做到动态的,多层包装。

注意,这里采用相同的超类,实际上只是为了做到 类型匹配,而装饰者并没有从超类中继承它的 行为

继承的原罪在于, 在运行时,行为需要改变! 如果行为从继承中得到,那么它在编译时就会被确定,也就是所谓的 与具体实现绑定

但是,如果行为不从继承中得到,那么继承反而成了优点,因为继承可以实现多态,为我们的动态扩展提供合适的条件

# 4. 特点

  1. 装饰者和被装饰对象有 相同的超类型
  2. 可以使用一个或者 多个 装饰者来包装对象
  3. 由于有相同的超类型,所以在需要被装饰对象的场合时,可以使用装饰过后的对象来替代
  4. 装饰者可以在所委托被装饰者的行为之前、之后,加上自己的行为,以达到特定的目的
  5. 对象可以在任何时候被装饰,可以在运行时,动态地、不限量地对对象进行装饰

# 5. UML 图解

可以看到,装饰者和主体对象有一个 共同的超类

同时, DecoratorComponent 都是 抽象类

对于实际的装饰者,他们都包含了一个 Component 实例,这就是被其装饰的对象,通过多态来进行方法委托。

图中的 wrappedObject 需要从外部获得,一般来说,是通过 构造函数 传入的。

# 6. 例子图解

# 7. 真实的装饰者模式——Java IO

Java IO 库中大量使用了装饰者模式,这也就是为什么会出现如下的代码:

InputStream in =
    new BufferdInpuStream(new FileInputSteram());
1
2

可以看到 FileInputSteram 是被装饰的主体对象,而 BufferdInpuStream 是装饰对象。

# 8. 缺陷

装饰者对象的缺陷很明显,就是会 增加大量的小对象

同时,由于装饰者模式是通过 层层委托 来实现扩展的;

所以,当装饰者需要改变的时候,就需要将改变应用到 所有的装饰者

此时,当装饰者数量较多时,更改难度大。