装饰器模式在 Java IO 中的应用
Java IO 类可分为下面四个维度:
而根据不同的读取和使用场景 Java IO 又在这四个父类基础之上,扩展出了很多子类:
但我们重点关注上图中最右边的一个类 BufferedInputStream,首先看下它的用法:
1
2
3
4
5
6
InputStream in = new FileInputStream("/user/wangzheng/test.txt");
InputStream bin = new BufferedInputStream(in);
byte[] data = new byte[128];
while (bin.read(data) != -1) {
//...
}
实际上 BufferedInputStream 是一个支持带缓存功能的数据读取类,可以提高数据读取的效率。但是按照平常的思维,我们会设计一个继承自 FileInputStream 的类,根据哪个方法需要缓存功能去自定义实现,但事实上,BufferedInputStream 并没有去继承 FileInputStream
为什么
前面我们学过组合优于继承,而 Java IO 的设计确实不能大量使用继承,因为根据基于以上四个父类派生了大量的子类,而每个子类都需要缓存功能,或者是其他很多功能的话,使用继承就会让类的继承关系变得异常复杂和冗余。
而 Java IO 的设计恰恰是巧妙的应用了装饰器模式,实现方式具体可查看 JDK 源码,实际上它的实现方式就跟代理模式一模一样,只不过二者的区别是:代理模式常用与在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志,而装饰器模式附加的增强功能往往跟原始类是相关的。
总结
装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承。它主要的作用是给原始类添加增强功能。这也是判断是否该用装饰器模式的一个重要的依据。除此之外,装饰器模式还有一个特点,那就是可以对原始类嵌套使用多个装饰器。为了满足这个应用场景,在设计的时候,装饰器类需要跟原始类继承相同的抽象类或者接口。
1
2
3
4
5
6
7
/**
* 嵌套使用
*/
InputStream in = new FileInputStream("/user/wangzheng/test.txt");
InputStream bin = new BufferedInputStream(in);
DataInputStream din = new DataInputStream(bin);
int data = din.readInt();