4.4 DECORATOR(装饰) — 对象结构型模式

1 意图

动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更加灵活。

2 别名

包装器Wrapper

3 动机

有时我们希望给某个对象而不是整个类添加一些功能。例如,一个图形用户界面工具箱允许你对任意一个用户界面组件添加一些特性,例如边框,或者一些行为,例如窗口滚动。

使用继承机制是添加功能的一种有效途径,但不够灵活,因为边框的选择的静态的,用户不能控制对组件加边框的方式和时机。

一种较为灵活的方式是将组件嵌入另一个对象中,由这个对象添加边框。我们称这个嵌入的对象为装饰。这个装饰与它所装饰的组件接口一致,因此它对使用该组件的客户透明。它将客户请求转发给该组件,并且可能在转发前后执行一些额外的动作。透明性使得你可以递归的嵌套多个装饰,从而可以添加任意多的功能,如下图所示:


image.png

例如,假定有一个对象TextView,它可以在窗口中显示正文。只需要简单地将这些装饰和TextView进行组合,就可以达到预期的效果。

下面的对象图展示了如何将一个TextView对象与BorderDecorator以及ScrollDecorator对象组装起来产生一个具有边框和滚动条的文本显示窗口。


image.png

ScrollDecorator和BorderDecorator类是Decorator类的子类,Decorator类是一个可视组件的抽象类,用于装饰其它可视组件,如下图:


image.png

VisualComponent是一个描述可视对象的抽象类,它定义了绘制和事件处理的接口。注意Decorator类怎样将绘制请求简单地发送给它的组件,以及Decorator的子类如何扩展这个操作。

Decorator的子类为特定功能可以自由地一些操作。例如,如果其它对象知道界面中恰好有一个ScrollDecorator对象,这些对象就可以用ScrollDecorator对象的ScrollTo操作滚动这个界面。这个模式中有一点很重要,它使得在 VisualComponent可以出现的任何地方都可以有装饰。因此,客户通常不会感觉到装饰过的组件与未装饰组件之间的差异,也不会与装饰产生任何依赖关系。

4 适用性

以下情况使用Decorator模式:

  • 1 在不影响其它对象的情况下, 以动态、透明的方式给单个对象添加职责;
  • 2 处理那些可以撤销的职责;
  • 3 当不能采用生成子类的方法进行扩充时,一种情况是,可能有大量独立的扩展,为支持每一种组合产生大量的子类,使得子类数目呈爆炸性增长。另一个情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
5 结构
image.png
6 参与者
  • Component(VisualComponent)
    ——定义一个对象接口,可以给这些对象动态地添加职责
  • ConcreteComponent(TextView)
    ——定义一个对象,可以给这个对象动态地添加职责
  • Decorator
    ——维持一个指向Component对象的指针,并定义一个与Component接口一致的接口。
  • ConcreteDecorator(BorderDecorator ScrollDecorator)
    ——向组件添加职责
7 协作

Decorator将请求转发给它的Component对象,并有可能在转发请求前后执行一些附加的动作。

8 效果

Decorator模式至少有两个主要优点和缺点:

  • 1 比静态继承更灵活 与对象的静态继承(多重继承)相比,Decorator模式提供了更加灵活的向对象添加职责的方式。
  • 2 避免在层次结构高层的类有太多的特征
  • 3 Decorator与它的Component不一样:Decorator是一个透明的包装。如果我们从对象标识的观点出发,一个被装饰了的组件与这个组件是有差别的,因此,使用装饰时不应该依赖对象标识。
  • 4 有许多小对象:采用Decorator模式进行系统设计往往会产生许多看上去类似的小对象,这些对象仅仅在他们相互连接的方式上有所不同,而不是它们的类或是它们的属性值有所不同。尽管对于那些了解这些系统的人来说,很容易对它们进行定制,但是很难学习这些系统,排错也很困难。
9 实现

使用Decorator模式时应注意以下几点:

  • 1 接口的一致性:装饰对象的接口必须与它所装饰的Component的接口一致,因此,所有ConcreteDecorator类必须有一个公共的父类;
  • 2 省略抽象的Decorator类:当你仅需要添加一个职责时,没有必要定义抽象Decorator类。
  • 3 保持Component类的简单性;
  • 4 改变对象外壳与改变对象内核。
10 代码示例

github地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容