装饰者模式有一个设计非常巧妙的结构,可以为对象动态添加功能。在基本的设计原则中,有一个重要的原则叫做合成/聚合复用原则。根据该原则的思想,代码复用应该尽可能使用委托,而不是继承。
因为继承是一种紧密耦合,任何父类的改动都会影响其子类,不利于系统维护。而委托则是松散耦合,只要接口不变,委托类的变动并不会影响其上层维护对象。

装饰者模式充分运用了这种思想,通过委托机制,复用系统的各个组件,在运行时,将这些组件功能进行叠加,从而构造出一个“超级对象”,使其拥有所有这些组件的功能。
而各个子功能模块,被很好的维护在各个组件的相关类中,拥有简洁的系统结构。

下面以一个例子来说明。
IPacketCretor即装饰者接口,用于处理具体的内容。PacketBodyCreator是具体的组件,用于构造要发布的信息的核心内容,但是它不负责将其构造成一个格式工整、可直接发布的数据格式。
PacketHTTPHeaderCreator负责给具体的内容加上HTTP头部,PacketHTMLHeaderCreator负责将给定的内容格式化成HTML文本。3个功能组件相互独立且分离,便于系统维护。

IPacketCreator

1
2
3
4
5
6
/**
* Created by j.tommy on 2017/9/14.
*/
public interface IPacketCreator {
public String handleContent(); // 用于处理具体内容
}

PacketBodyCreator

1
2
3
4
5
6
7
8
9
/**
* Created by j.tommy on 2017/9/14.
*/
public class PacketBodyCreator implements IPacketCreator {
@Override
public String handleContent() {
return "Content of packet.";
}
}

PacketDecorator

1
2
3
4
5
6
7
8
9
/**
* Created by j.tommy on 2017/9/14.
*/
public abstract class PacketDecorator implements IPacketCreator {
IPacketCreator ipc;
public PacketDecorator(IPacketCreator ipc) {
this.ipc = ipc;
}
}

PacketHTMLHeaderCreator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Created by j.tommy on 2017/9/14.
*/
public class PacketHTMLHeaderCreator extends PacketDecorator {
public PacketHTMLHeaderCreator(IPacketCreator ipc) {
super(ipc);
}
@Override
public String handleContent() {
StringBuffer buffer = new StringBuffer();
buffer.append("<html>");
buffer.append("<body>");
buffer.append(ipc.handleContent());
buffer.append("</body>");
buffer.append("</html>");
return buffer.toString();
}
}

PacketHTTPHeaderCreator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* Created by j.tommy on 2017/9/14.
*/
public class PacketHTTPHeaderCreator extends PacketDecorator {
public PacketHTTPHeaderCreator(IPacketCreator ipc) {
super(ipc);
}
@Override
public String handleContent() {
StringBuffer buffer = new StringBuffer();
buffer.append("Cache-Control:no-cache\n");
buffer.append(ipc.handleContent());
return buffer.toString();
}
}

测试类Main

1
2
3
4
5
6
7
8
9
/**
* Created by j.tommy on 2017/9/14.
*/
public class Main {
public static void main(String[] args) {
IPacketCreator ipc = new PacketHTTPHeaderCreator(new PacketHTMLHeaderCreator(new PacketBodyCreator()));
System.out.println(ipc.handleContent());
}
}

输出:

对于装饰者模式,另一个值得关注的地方是它的使用方法。在本例中,通过层层构造和组装装饰者与被装饰者到一个对象中,使其有机的结合在一起工作。

在本例中,共生成3个对象实例,PacketBodyCreator作为核心组件被首先 构造,其次是PacketHTMLHeaderCreator(将内容包装成HTML格式),最后是PacketHTTPHeaderCreator(添加HTTP头)。

在JDK的实现中,也有装饰者模式的实现。一个典型的例子就是OutputStream和InputStream的实现。以OutputStream为例,OutputStream提供的功能较弱,通过各种装饰器的增强,OutputStream可以被赋予强大的功能。

生成一个有缓冲功能的流对象

1
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("d:/test.txt")));

生成一个没有缓冲功能的流对象

1
DataOutputStream dos = new DataOutputStream(new FileOutputStream("d:/test.txt"));

第1种加入了性能组件BufferedOutputStream,第二种则没有。因此第1种拥有更好的性能。

在BufferedOutputStream中,并不是每次调用write方法都会向磁盘写入数据,而是将数据写入缓冲,只有缓冲满的时候才会调用FileOutputStream的write方法向磁盘写入,以此实现功能组件与性能组件的完美分离。

参考:《《Java程序性能优化-让你的Java程序更快、更稳定》(葛一宁等编著)》