装饰器(Decorator)模式 可以在不改变原有对象的情况下拓展其功能。
装饰器模式通过组合替代继承来扩展原始类的功能,在一些继承关系比较复杂的场景(IO 这一场景各种类的继承关系就比较复杂)更加实用。
对于字节流来说, FilterInputStream
(对应输入流)和FilterOutputStream
(对应输出流)是装饰器模式的核心,分别用于增强 InputStream
和OutputStream
子类对象的功能。
我们常见的BufferedInputStream
(字节缓冲输入流)、DataInputStream
等等都是FilterInputStream
的子类,BufferedOutputStream
(字节缓冲输出流)、DataOutputStream
等等都是FilterOutputStream
的子类。
举个例子,我们可以通过 BufferedInputStream
(字节缓冲输入流)来增强 FileInputStream
的功能。
BufferedInputStream
构造函数如下:
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];
}
可以看出,BufferedInputStream
的构造函数其中的一个参数就是 InputStream
。
BufferedInputStream
代码示例:
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("input.txt"))) {
int content;
long skip = bis.skip(2);
while ((content = bis.read()) != -1) {
System.out.print((char) content);
}
} catch (IOException e) {
e.printStackTrace();
}
这个时候,你可以会想了:为啥我们直接不弄一个BufferedFileInputStream
(字符缓冲文件输入流)呢?
BufferedFileInputStream bfis = new BufferedFileInputStream("input.txt");
如果 InputStream
的子类比较少的话,这样做是没问题的。不过, InputStream
的子类实在太多,继承关系也太复杂了。如果我们为每一个子类都定制一个对应的缓冲输入流,那岂不是太麻烦了。
如果你对 IO 流比较熟悉的话,你会发现ZipInputStream
和ZipOutputStream
还可以分别增强 BufferedInputStream
和 BufferedOutputStream
的能力。
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileName));
ZipInputStream zis = new ZipInputStream(bis);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fileName));
ZipOutputStream zipOut = new ZipOutputStream(bos);
ZipInputStream
和ZipOutputStream
分别继承自InflaterInputStream
和DeflaterOutputStream
。
public
class InflaterInputStream extends FilterInputStream {
}
public
class DeflaterOutputStream extends FilterOutputStream {
}
这也是装饰器模式很重要的一个特征,那就是可以对原始类嵌套使用多个装饰器。
为了实现这一效果,装饰器类需要跟原始类继承相同的抽象类或者实现相同的接口。上面介绍到的这些 IO 相关的装饰类和原始类共同的父类是 InputStream
和OutputStream
。
对于字符流来说,BufferedReader
可以用来增加 Reader
(字符输入流)子类的功能,BufferedWriter
可以用来增加 Writer
(字符输出流)子类的功能。
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName), "UTF-8"));