我们已经学习了结构型模式中的适配器模式、桥接模式和组合模式。而今天我们要介绍的装饰模式看上去和适配器模式、桥接模式很相似,都是使用组合方式来扩展原有类的,但其实本质上却相差甚远呢。
简单来说,适配器模式侧重于转换,而装饰模式侧重于动态扩展;桥接模式侧重于横向宽度的扩展,而装饰模式侧重于纵向深度的扩展
装饰模式的原始定义是:允许动态地向一个现有的对象添加新的功能,同时又不改变其结构,相当于对现有的对象进行了一个包装。
这个定义非常清晰易懂,因为不能直接修改原有对象的功能,只能在外层进行功能的添加,所以装饰模式又叫包装器模式。
下面我们还是直接来看看装饰模式的 UML 图:
从 UML 图中,我们能发现装饰模式的四个关键角色。
组件:作为装饰器类包装的目标类。
具体组件:实现组件的基础子类。
装饰器:一个抽象类,其中包含对组件的引用,并且还重写了组件接口方法。
具体装饰器:继承扩展了装饰器,并重写组件接口方法,同时可以添加附加功能。
接下来我们再来看看它的代码实现:
//组件
public interface Component {
void excute();
}
//具体组件
public class BaseComponent implements Component {
@Override
public void excute() {
//do something
}
}
//装饰器
public class BaseDecorator implements Component {
private Component wrapper;
public BaseDecorator(Component wrapper) {
this.wrapper = wrapper;
}
@Override
public void excute() {
wrapper.excute();
}
}
//具体装饰器A
public class DecoratorA extends BaseDecorator {
public DecoratorA(Component wrapper) {
super(wrapper);
}
@Override
public void excute() {
super.excute();
}
}
//具体装饰器B
public class DecoratorB extends BaseDecorator {
public DecoratorB(Component wrapper) {
super(wrapper);
}
@Override
public void excute() {
super.excute();
}
}
这段代码实现比较简单,组件 Component 定义了组件具备的基本功能,具体组件 BaseComponent 是对组件(接口)的一种基础功能的实现,装饰器 BaseDecorator 中包含 Component 的抽象实例对象,作为装饰器装饰的目标对象,具体装饰器 DecoratorA 和 DecoratorB 继承装饰器 BaseDecorator 来进行具体附加功能的沿用与扩展。
所以说,装饰模式本质上就是给已有不可修改的类附加新的功能,同时还能很方便地撤销。
使用场景分析
一般来讲,装饰模式常用的使用场景有以下几种。
快速动态扩展和撤销一个类的功能场景。 比如,有的场景下对 API 接口的安全性要求较高,那么就可以使用装饰模式对传输的字符串数据进行压缩或加密。如果安全性要求不高,则可以不使用。
可以通过顺序组合包装的方式来附加扩张功能的场景。 比如,加解密的装饰器外层可以包装压缩解压缩的装饰器,而压缩解压缩装饰器外层又可以包装特殊字符的筛选过滤的装饰器等。
不支持继承扩展类的场景。 比如,使用 final 关键字的类,或者系统中存在大量通过继承产生的子类。