装饰器模式 — Java IO
Java IO中含有众多的类,对于刚接触的人来说往往摸不清头脑。其实只要理清了它们之间的层次关系,就容易理解很多。
Java IO中涉及到装饰器模式(Decorator Pattern)与适配器模式(Adapter Pattern)。先上一张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有如此神奇的功效呢?这就是装饰者模式的功劳。装饰者模式结构如下:
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 图(图片来源网络)