本文主要是通过Java I/O学习装饰器模式。
一、装饰器模式的定义和实现
定义:指在不改变现有对象结构的情况下,动态地给该对象增加额外功能的模式。
实现:装饰类Decorator为ConcreteApple新增功能resA()、resB()
public class ConcreteApple{
public void res() {
// ...
}
}
public class Decorator implements ConcreteApple{
protected ConcreteApple apple;
public Decorator(ConcreteApple apple) {
super();
this.apple = apple;
}
public void res() {
resA(); // 扩展功能A
apple.res();
resB(); // 扩展功能B
}
public void resA() {
// ...
}
public void resB() {
// ...
}
}
二、Java I/O的装饰器模式
Java I/O类图如下所示。
I/O类的使用如下所示,FileInputStream具有读取文件的功能,BufferedInputStream具有缓存功能。FileInputStream作为BufferedInputStream的构造参数,感觉有点问题:创建了两次InputStream,一层嵌套一层。
InputStream in = new FileInputStream("/user/wangzheng/test.txt");
InputStream bin = new BufferedInputStream(in);
byte[] data = new byte[128];
while (bin.read(data) != -1) {
//...
}
问题1:为什么需要嵌套InputStream?
解答:我们需要读取文件,也需要有缓存功能
问题2:为什么不直接设计读取文件+缓存功能的类,如BufferedFileInputStream?
解答:如果直接设计BufferedFileInputStream,可能导致类太多。
InputStream bfin = new BufferedFileInputStream("/user/wangzheng/test.txt");
while (bfin.read(data) != -1) {
//...
}
针对问题2,我们一起来看看,如何直接设计BufferedFileInputStream:可以采用继承的方案。思考一下,如上图1,InputStream有4个数据源子类+2个功能子类(非抽象类),分别是ByteArrayInputStream、PipeInputStream、FileInputStream、ObjectInputStream和BufferedInputStream、DatainputStream。
基于继承的方案:BufferedByteArrayInputStream、BufferedPipeInputStream等,总共4*2个类。如果功能子类数量增加,例如OutputStream的BufferedOutputStream、DataOutputStream、PrintOutputStream,导致类数量的爆炸。
因此,可以采用装饰器模式,符合“组合优于继承”,可以“使用组合来替代继承。
装饰器模式的特点
1.继承相同的父类:装饰器类和原始类继承同样的父类,这样我们可以对原始类“嵌套”多个装饰器类。
2.增强效果:装饰器类是对功能的增强,这也是装饰器模式应用场景的一个重要特点。
文章参考王争老师的《设计模式之美》。