定义
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式比生成子类更为灵活。
结构和说明
示例代码
public class DecoratorDemo {
/**
* 组件对象的接口,可以给这些对象动态添加职责
*/
public static abstract class Compoent {
/**
* 示例方法
*/
public abstract void operation();
}
/**
* 具体实现组件对象接口的对象
*/
public static class ConcreteCompoent extends Compoent {
@Override
public void operation() {
// 相应功能处理
}
}
/**
* 装饰器接口,维持一个指向组件对象,并定义一个与组件接口一致的接口
*/
public static abstract class Decorator extends Compoent {
/**
* 持有组件对象
*/
protected Compoent compoent;
public Decorator(Compoent compoent) {
this.compoent = compoent;
}
@Override
public void operation() {
// 转发请求给组件对象,可以在转发前后执行一些附加工作
compoent.operation();
}
}
/**
* 装饰器的具体实现对象,想组件负责添加职责
*/
public static class ConcreteDecoratorA extends Decorator {
/**
* 添加的状态
*/
@Getter
@Setter
private String addedState;
public ConcreteDecoratorA(Compoent compoent) {
super(compoent);
}
@Override
public void operation() {
// 调用父类的方法,可以在调用前后执行一些附加动作
// 在这里进行处理的时候,可以使用添加的状态
super.operation();
}
}
/**
* 装饰器的具体实现对象,想组件负责添加职责
*/
public static class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Compoent compoent) {
super(compoent);
}
@Override
public void operation() {
// 调用父类的方法,可以在调用前后执行一些附加动作
super.operation();
addedBehavior();
}
private void addedBehavior() {
// 需要添加的职责实现
}
}
}
优缺点
优点
- 比继承更灵活
从为对象添加功能的角度来看,装饰模式比继承更灵活。继承是静态的,而且一旦继承所有子类都有一样的功能。而装饰模式采用把功能分离到每个装饰器当中,然后使用对象组合的方式,在运行时动态地组合功能,每个被装饰的对象最终有哪些功能,是由运行期到动态组合的功能来决定的。
- 更容易复用功能
装饰模式把一系列复杂的功能分散到每个装饰器当中,一般一个装饰器只实现一个功能,使实现装饰器变得简单,更重要的是这样有利于装饰器功能的复用,可以给一个对象增加多个同样的装饰器,也可以把一个装饰器用来装饰不同的对象,从而实现复用装饰器的功能。
- 简化高层定义
装饰模式可以通过组合装饰器的方式,为对象添加任意多的功能。因此在进行高层定义的时候,不用把所有的功能都定义出来,而是定义最基本的就可以了,可以在需要使用的时候,组合相应的装饰器来完成所需的功能。
缺点
- 会产生很多细粒度对象
前面说了,装饰模式是把一系列复杂的功能,分散到每个装饰器当中,一般一个装饰器只实现一个功能,这样会产生很多细粒度的对象,而且功能越复杂,需要的细粒度对象越多。
思考
本质
动态组合。
何时选用
- 如果需要在不影响其他对象的情况下,以动态、透明的方式给对象添加职责,可以使用装饰模式,这几乎是装饰模式的主要功能。
- 如果不适合使用子类进行扩展的时候,可以考虑使用装饰模式。因为装饰模式是使用的“对象组合”的方式。所谓不适合用子类扩展的方式,比如,扩展功能需要的子类太多,造成子类数目呈爆炸性增长。
相关模式
- 装饰模式与适配器模式
这是两个没有什么关联的模式,放到一起来说,是因为他们有一个共同的别名:Wrapper。
这两个模式功能上是不一样的,适配器模式是用来改变接口的,而装饰模式是用来改变对象功能的。
- 装饰模式与组合模式
这两个模式有相似之处,都涉及到对象的递归调用,从某个角度来说,可以把装饰看做是只有一个组件的组合。
但是它们的目的完全不一样,装饰模式是要动态地给对象增加功能;而组合模式是想要管理组合对象和叶子对象,为它们提供一个一致的操作接口给客户端,方便客户端的使用。
- 装饰模式与策略模式
这两个模式可以组合使用。
策略模式也可以实现动态地改变对象的功能,但是策略模式只是一层选择,也就是根据策略选择一下具体实现类而已。而装饰模式不是一层,而是递归调用,无数层都可以,只要组合好装饰器的对象组合,那就可以依次调用下去。所以装饰模式更灵活。
而且策略模式改变的是原始对象的功能,不像装饰模式,后面一个装饰器,改变的是经过前一个装饰器装饰后的对象。也就是策略模式改变的是对象的内核,而装饰模式改变的是对象的外壳。
这两个模式可以组合使用,可以在一个具体的装饰器中使用策略模式来选择更具体的实现方式。
- 装饰模式与模版方法模式
这是功能上有点相似的模式。
模版方法模式主要应用在算法骨架固定的情况,那么要是算法步骤不固定呢,也就是一个相对动态的算法步骤,就可以使用装饰模式了,因为在使用装饰模式的时候,进行装饰器组装,其实相当于是一个调用算法步骤的组装,相当于是一个动态的算法骨架。
既然装饰模式可以实现动态的算法步骤的组装和调用,那么把这些算法步骤固定下来,那就是模版方法模式实现的功能了,因此装饰模式可以模拟实现模版方法模式的功能。
但是请注意,仅仅只是可以模拟功能而已,两个模式的设计目的、原本的功能、本质思想等都是不一样的。