设计模式 --- 装饰者模式

423 阅读3分钟

java io 中的装饰者模式


装饰者模式可以对原本方法进行增强,例如 java io 中的 FileInputStream 没有缓存功能,执行速度慢,利用 BufferedInputStream 对 FileInputStream 进行装饰就能实现有缓存功能的 io 流方法,按照这个思路,我们手动写一个 BufferedInputStream 

public class BufferedInputStream extends FileInputStream {
    
    byte[] buffer = new byte[8192];
    
    @Override
    public int read(){
    	// 调用父类的read方法,添加缓存的逻辑
    }
}


装饰者模式有两种编程方式,继承和组合,在 java io 中使用的是不使用继承的方式,这是因为 InputStream 的实现类实在是有很多,如果一一继承的话会导致类的继承结构变得非常复杂。如果使用组合的方式的话,也会有很多麻烦,如下所示
public class BufferedInputStream {
    
    private InputStream inputStream;
    
    public BufferedInputStream(InputStream inputStream){
    	this.inputStream = inputStream;
    }
    
    byte[] buffer = new byte[8192];
    
    public int read(){
    	// 调用父类的read方法,添加缓存的逻辑
    }
    
    // 其他不需要进行增强的方法
    public void other(){
    	inputStream.other();
    }
}

如果用组合的方式,其他不需要增强的方法也要全部重写实现,在方法体中调用组合的方法来返回,代码的重复性很高,因此,它使用了一种很巧妙的方法,就是 组合 + 继承 

既然使用继承,那继承的 FileInputStream 肯定不行,继承的坏处依旧没有解决呀,如果继承 InputStream 的话,组合的坏处依旧没有解决,仍需全部重写并调用组合类中的方法。查看 jdk 源码,发现它继承的是 FilterInputStream,那它是如何解决这个问题的呢,我们来看源代码

public class FilterInputStream extends InputStream {
    
    protected volatile InputStream in;

    protected FilterInputStream(InputStream in) {
        this.in = in;
    }

    public int read() throws IOException {
        return in.read();
    }

    public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }

    public int read(byte b[], int off, int len) throws IOException {
        return in.read(b, off, len);
    }

    public long skip(long n) throws IOException {
        return in.skip(n);
    }

    // 其他方法类似,都是调用组合类中的方法
}

这个设计就非常巧妙,把组合类放到 FilterInputStream 中, FilterInputStream 中全部重新实现类组合类中的方法,然后 BufferedInputStream 再继承它,这样 BufferedInputSteam 只要对需要缓存的方法进行增强就行了,不用关心其他不需要增强的方法。

java io 中不只有对缓存的增强,还有 ZipInputStream 压缩流、 DataInputStream 数据流等,这些都继承了 FilterInputStream`,这样这些类只需关心自己需要进行增强的方法即可



装饰者模式比代理模式特殊的地方


如果把装饰者模式和静态代理进行比较,会发现它们都是对原始方法的增强,那装饰者模式有什么特殊的地方呢

1、代理模式中,代理类增加的是与原始方法无关的内容,而装饰者模式是对原始方法相关功能的增强

2、装饰者模式包装类都继承了同一个对象 FilterInputStream 或 FilterOutputStream ,这两个类又继承了 InputStream 或 OutputStream,因此它们能进行嵌套,如下所示

FileInputStream in = new FileInputStream("D://xxx.xml");
BufferedInputStream buf = new BufferedInputStream(in);
ZipInputStream zip = new ZipInputStream(buf);