为什么会有装饰者模式
假如有一个这样的场景,我们需要为某些对象(A)动态的添加一些功能(method),那我们会怎么做?很直观的我们会想到继承,写一个类(B)继承于这个对象所属的类(A),然后重写他的方法。这似乎解决了问题,但是如果有一天,对象A多了一个功能或对原有的功能进行了修改,这个时候你会发现类B或多或少也要做出一些改变。这开始变得有些不方便,逐渐的人们发现继承这样的紧密耦合的方式在代码复用方面并不理想。那么如何才能更好的复用代码使得对象的功能能够不断增加完善呢?接口是不错的选择,只要接口不发生变化,那么我们就可以通过某种设计对实现这个接口的类动态的添加某些功能,这种设计就是装饰者模式。
装饰者模式的特点和结构
通过上述描述,我们可以发现装饰者模式最大的特点就是装饰者和被装饰者都拥有相同的接口。装饰者可以在被装饰者方法的前后加上特定的前置处理和后置处理来增强被装饰者的功能。
装饰者模式实例
JAVA IO中的以OutputStream为核心的装饰者模式。如果我们向一个文件中写入数据可以用以下的代码
DataOutputStream dataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("文件路径")));
dataOutputStream.writeDouble(8.8);
dataOutputStream.flush();
这种通过嵌套的方式使得对象可以不断的聚合起来,最终称为一个功能强大IO类。装饰模式主要就是使用聚合。类图如下面所示,
那到底什么是聚合,一句话概括就是影魔和灵魂的关系,灵魂是影魔的一部分,但是没有灵魂了影魔还是可以玩的。那经常裹在一起的组合又是什么呢?在用一句话概括吧,蓝猫和蓝的关系,蓝猫需要蓝,没有蓝还玩个虚空。所以虽然组合和聚合都是整体和部分的关系,但是组合的关系更加紧密,更强一些。
最后附上一张图和两行代码从java的角度解释下聚合。
//DataOutputStream的构造方法
public DataOutputStream(OutputStream out) {
super(out);
}
这里通过传参的方式使得OutputStream成为了DataOutputStream的一部分,但是DataOutputStream对象的生死却对OutputStream对象的生命周期产生什么影响,所以这是聚合,如果换成以下的方式,就是组合了,因为咱们生死与共啊。
public DataOutputStream() {
out = new XXX();
}