Decorator(装饰)模式(对象结构型模式)
一. 意图
动态地给一个对象添加一些额外的职责. 就增加功能来说, Decorator模式相比生成子类更为灵活.
二. 适用性
1. 在不影响其他对象的情况下, 以动态, 透明的方式给单个对象添加职责.
2. 处理那些可以撤消的职责.
3. 当不能采用生成子类的方法进行扩充时. 一种情况是, 可能有大量独立的扩展, 为支持每一种组合将产生大量的子类, 使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏, 或类定义不能用于生成子类.
三. 模式结构
图1
四. 角色说明
Component
—定义一个对象接口,可以给这些对象动态地添加职责。
ConcreteComponent
—定义一个对象,可以给这个对象添加一些职责。
Decorator
—维持一个指向Component对象的指针, 并定义一个与Component接口一致的接口.
ConcreteDecorator
—向组件添加职责
Decorator将请求转发给它的Component对象, 并有可能在转发请求前后执行一些附加的动作.
五. 说明
对一个已经封装好的功能增加功能, 因为已经封装好了, 你不可能在它的内部进行修改. 现在就有两种方式:
1. 使用继承, 生成一个该功能的子类. 这个子类的要求肯定是要实现父类的接口(父类公开出来的方法).(因为父类已经在用了).
然后对父类对应的方法进行功能扩展.(在父类的方法里面你喜欢怎样就怎样了).
2. 生成一个类, 该类包含该原功能类的对象. 这个类同样实现原功能类的接口.(新类中对应的接口调用对象对应的接口).
(新类与功能类继承与同一接口, 这样原来的用户就不需要改动代码.)(这就是Decorator模式了)
使用Decorator模式时应注意以下几点:
1. 接口的一致性 装饰对象的接口必须与它所装饰的Component的接口是一致的, 因此,所有的ConcreteDecorator类必须有一个公共的父类.
2. 没有抽象的Decorator类, 因为你常常需要处理现存的类层次结构而不是设计一个新系统, Decorator与ConcreteComponent位于同一类层次即可(继承于同一父类).
3. 保持Component类的简单性 为了保证接口的一致性, 组件和装饰必须有一个公共的Component父类. 因此保持Component类的简单性是很重要的: 即它应集中于定义接口而不是存储数据. 对数据表示的定义应延迟到子类中, 否则Component类会变得过于复杂和庞大, 因而难以大量使用。赋予Component太多的功能也使得,具体的子类有一些它们并不需要的功能的可能性大大增加。
4. 改变对象外壳与改变对象内核, 我们可以将Decorator看作一个对象的外壳, 它可以改变这个对象的行为. 另外一种方法是改变对象的内核. 例如Strategy模式就是一个用于改变内核的很好的模式.
六. 我的理解
1. 有一个类, 你不能改变它的内部实现, 但是需要扩展它的功能.你可以使用一个新类, 新类包含一个原类的对象, 在调用对象的方法前后扩展它的功能. 有一个问题, 原类已经再使用了, 它的接口已经被客户使用了. 我们扩展它的功能的前提是尽量的让客户少改代码, 少改变. 如果原类的功能方法不是继承接口的(原类没有父类, 或者说原类提供给客户的方法不是接口), 那Decorator模式无法使用的. ConcreteComponent与Decorator必须有相同的接口(要扩展功能所对应的那个接口).
图2
2. Decorator中包含一个ConcreteComponent对象, Decorator::Operation中调用ConcreteComponent::Operation.
而ConcreteDecorator::Operation调用Decorator::Operation (可以看到ConcreteComponent与ConcreteDecorator被Decorator隔开了解耦了).
这样隔开, 效果非常明显在类层次上, 我可以横向的扩展ConcreteComponent的功能(增加ConcreteDecorator类). 如果使用纵向扩展(类继承), 一条长长的类继承链, That's not a good idea.
3. Decorator中定义的是一个Component指针, 而不是一个ConcreteComponent指针, 也就是Decorator不是针对某个ConcreteComponent类扩展, 而是可以针对所有的ConcreteComponent来扩展.(这一点可以区分Proxy代理模式: 它就是针对某个类的)
4. ConcreteDecorator::Operation中你可以根据你的需求随便修改, 甚至Decorator::Operation你也可以不调用, 看需求吧.
七. 相关模式
1. Adapter模式:Decorator模式不同于Adapter模式, 因为装饰仅改变对象的职责而不改变它的接口; 而适配器将给对象一个全新的接口.
2. Composite模式: 可以将装饰视为一个退化的, 仅有一个组件的组合. 然而, 装饰仅给对象添加一些额外的职责—它的目的不在于对象聚集.
3. Strategy模式: 用一个装饰你可以改变对象的外表; 而Strategy模式使得你可以改变对象的内核. 这是改变对象的两种途径.