• 装饰器模式


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

      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) {
    	//...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

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

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

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

      在这种情况下,如果我们继续按照继承的方式来实现的话,就需要再继续派生出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;
    	}
    	 //...实现读取基本类型数据的接口
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

      从上面的 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();
    
    • 1
    • 2
    • 3
    • 4

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

  • 相关阅读:
    tiup cluster stop
    二叉树相关算法
    软件定义网络-OpenvSwitch
    ChatGPT提效:告别CRUD
    VB.net进行CAD二次开发(四)
    一个程序员的成长之路
    C++可调用对象的绑定器和包装器
    lodash的merge()方法
    从白手起家到人尽皆知,他的大佬程序员之路是怎样走向成功的?
    JAVA泛型
  • 原文地址:https://blog.csdn.net/qq_43654226/article/details/126512873