装饰器模式 — Java IO

Java IO中含有众多的类,对于刚接触的人来说往往摸不清头脑。其实只要理清了它们之间的层次关系,就容易理解很多。

Java IO中涉及到装饰器模式(Decorator Pattern)与适配器模式(Adapter Pattern)。先上一张Java IO(字节流)系谱图(图片来源网络)。

装饰器模式 — Java IO装饰器模式 — Java IO

可以看到,输入和输出基本是高度对称的。
上图并没有包括所有的IO类,随着Java版本的变化,可能有新的类逐渐加入到这个大家族中,也可能有原有的类被废弃(StringBufferInputStream已废弃)。


装饰器模式

InputStream是输入流所有类的父类,由此产生了很多分支,每个分支的功能不同,比如FileInputStream用来读取文件信息、ByteArrayInputStream允许将内存的缓冲区当做InputStream使用、SequenceStream将两个或多个InputStream对象转换成单一的Inputstream。

这里我们重点关注FilterInputStream,即装饰类,它的子类可以用来装饰其它类型的InputStream。

比如现在我想读取C盘下的test.txt文件,我们会这样用:

FileInputStream f = new FileInputStream(new File("c:/test.txt"));

现在我嫌读取的太慢了,想给它加个缓冲的功能,怎么办呢?只需要给它套上一层外壳即可。

BufferedInputStream f = new BufferedInputStream(
    			new FileInputStream(new File("c:/test.txt")));

这里,BufferedInputStream就相当于外壳。当然,你也可以同时用多个装饰器,每个装饰器都有特定的功能。就好比我想吃蛋糕,但你可以给蛋糕同时加上草莓、芒果、樱桃作为点缀。

为什么FilterInputStream有如此神奇的功效呢?这就是装饰者模式的功劳。装饰者模式结构如下:

装饰器模式 — Java IO

InputStream就是图中的Component,FilterStream就是图中的Decorator,它的子类就是ConcreteDecoratorA、ConcreteDecoratorB、ConcreteDecoratorC…,其它的类就是ConcretComponent。

看一下FilterInputStream的构造器或许能更加了解这一点。

public class FilterInputStream extends InputStream {
    /**
     * The input stream to be filtered.
     */
    protected volatile InputStream in;
    
    protected FilterInputStream(InputStream in) {
        this.in = in;
    }
}

FilterInputStream构造器可以接受任意一种形式的InputStream。

以BufferedInputStream为例,它在构造器中调用了父类的构造器。

public class BufferedInputStream extends FilterInputStream {
    public BufferedInputStream(InputStream in) {
        this(in, DEFAULT_BUFFER_SIZE);
    }
    public BufferedInputStream(InputStream in, int size) {
        super(in);//调用父类构造器
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }
}

装饰者设计模式主要是利用多态,将子类对象作为参数传递,达到装饰的效果。如果不采用装饰者模式,而是采用继承结构,会造成类爆炸,假如有n种属性,那所有的搭配种类就有 2n 个。

这里讲的是 Java IO 中的字节流,除了字节流以外,还有字符流。字节流可以通过InputStreamReader或OutputStreamWriter转换成字符流,这就是所谓的适配器模式(见另一篇文章《适配器模式》)。

附上JAVA IO 图(图片来源网络)

装饰器模式 — Java IO