装饰器模式

如果大家觉得文章有错误内容,欢迎留言或者私信讨论~

  Java IO 类库非常庞大和复杂,有几十个类,负责 IO 数据的读取和写入。如果对 Java IO类做一下分类,我们可以从下面两个维度将它划分为四类。具体如下所示:
在这里插入图片描述
  针对不同的读写和写入常见, Java IO 又在四个父类基础只是,扩展了很多子类,具体如下所示:
在这里插入图片描述
  在我最初接触 java IO 的时候,就对 Java IO 的一些用法产生了很大的疑惑,比如要实现这样一个功能:打开文件 test.txt,从中读取数据。其中InputStream是一个抽象类,FileInputStream是专门用来读取文件流的子类。BufferedInputStream是一个支持带缓存功能的数据读取类,可以提高数据读取的效率,代码看起来是这样:

InputStream in = new FileInputStream("/user/wangzheng/test.txt");
InputStream bin = new BufferedInputStream(in);
byte[] data = new byte[128];初看上面的代码,我们会觉得 Java IO 的用法比较麻烦,需要先创建一个 FileInputStream
while (bin.read(data) != -1) {
	//...
}

  这最开始让我非常疑惑,为什么 Java IO 要做这么麻烦的操作,难道不能设计一个继承 FileInputStream 并且支持缓存的 BufferedFileInputStream 的类呢?这样我们就可以像下面的代码一样,直接创建一个 BufferedFileInputStream 类对象,使用起来不是更加简单吗?

InputStream bin = new BufferedFileInputStream("/user/wangzheng/test.txt");
byte[] data = new byte[128];
while (bin.read(data) != -1) {
	//...
}

  这样基于继承的解决方案, 如果 InputStream 只有一个子类FileInputStream的话,那么我们在它的继承上再设计一个子孙类,那么也还是可以接受的。但实际上,继承InputStream 的子类有很多,那么我们需要给这些子类都派生支持缓存读取的类。
  除了支持缓存读取之外,如果我们还需要对功能进行其他方面的增强,比如下面的DataInputStream类,支持按照基本数据类型(int、boolean、long 等)来读取数据:

FileInputStream in = new FileInputStream("/user/wangzheng/test.txt");
DataInputStream din = new DataInputStream(in);
int data = din.readInt();

  在这种情况下,如果我们继续按照继承的方式来实现的话,就需要再继续派生出DataFileInputStreamDataPipedInputStream 等类,这样慢慢的开发下去,一旦出现“破窗效应”,那就会组合爆炸,类继承结构变得无比复杂,代码既不好扩展,也不好维护。

基于装饰器模式的解决方案

  装饰器模式的思想是组合大于继承, 使用组合来替代继承。针对刚刚的继承结构过于复杂的问题,我们可以通过将继承关系改为组合关系来解决。下面的代码展示了 Java IO 的这种设计思路。不过,我对代码做了简化,只抽象出了必要的代码结构,如果你感兴趣的话,可以直接去查看 JDK 源码:

public abstract class InputStream {
	//...
	public int read(byte b[]) throws IOException {
		return read(b, 0, b.length);
	} 
	public int read(byte b[], int off, int len) throws IOException {
		//...
	} 
	public long skip(long n) throws IOException {
		//...
	} 
	public int available() throws IOException {
		return 0;
	} 
	public void close() throws IOException {}
	public synchronized void mark(int readlimit) {}
	public synchronized void reset() throws IOException {
		throw new IOException("mark/reset not supported");
	} 
	public boolean markSupported() {
		return false;
	}
}

public class BufferedInputStream extends InputStream {
	protected volatile InputStream in;
	protected BufferedInputStream(InputStream in) {
	this.in = in;
	} 
	//...实现基于缓存的读数据接口...
} 

public class DataInputStream extends InputStream {
	protected volatile InputStream in;
	protected DataInputStream(InputStream in) {
	this.in = in;
	}
	 //...实现读取基本类型数据的接口
}

  从上面的 Java io 设计来看,装饰器模式对于简单的组合关系,还有两个比较特殊的地方。第一个比较特殊的地方是:装饰器类和原始类继承同样的父类,这样我们可以对原始类“嵌套”多个装饰器类。 比如,下面这样一段代码,我们对 FileInputStream 嵌套了两个装饰器类:BufferedInputStream 和 DataInputStream,让它既支持缓存读取,又支持按照基本数据类型来读取数据:

InputStream in = new FileInputStream("/user/wangzheng/test.txt");
InputStream bin = new BufferedInputStream(in);
DataInputStream din = new DataInputStream(bin);
int data = din.readInt();

  第二个比较特殊的地方是:装饰器类是对功能的增强,这也是装饰器模式应用场景的一个重要特点。 相较于代理模式,装饰器模式的用途在于附加跟原始类相关的增强功能;而代理类模式则是附加跟原始类无关的功能。清晰两者的区分就能把握住如何去运用两种设计模式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值