装饰器模式在源码中应用的非常多,其中在 JDK 中体现最明显的类就是与 I/O 相关的类,如 BufferedReader、InputStream、OutputStream 及它们的子类。

    下面以 InputStream 为例进行讲解。

    • FileInputStream 是 InputStream 的子类,用来读取文件字节流
    • BufferedInputStream 是 InputStream 的子类 FilterInputStream 的子类,为可缓存的字节流
    • DataInputStream 也是FilterInputStream 的子类,可直接读取 Java 基本类型的字节流


    下面为 InputStream 的类图,如下所示。

    如果需要一个能够读取文件且可缓存的字节流,可以继承 BufferedInputStream。如果需要一个能够读取文件且直接读取基本类型的字节流,可以继承 FileDataInputStream。但是如果用继承方式,那类的层级与种类会多到爆炸。

    为了解决此类问题,使用了装饰器模式。

    InputStream 部分源码如下:FileInputStream 部分源码如下:BufferedInputStream 源码精简,继承 InputStream 类,如下:DataInputStream 源码精简,继承 InputStream 类,如下:组合各种类型的字节流,使用
    装饰器模式在JDK源码中的应用 - 图1
    InputStream 部分源码如下:

    1. public abstract class InputStream implements Closeable {
    2. public int read(byte b[], int off, int len) throws IOException {}
    3. }

    FileInputStream 部分源码如下:

    1. public class FileInputStream extends InputStream {
    2. public FileInputStream(String name) throws FileNotFoundException {
    3. this(name != null ? new File(name) : null);
    4. }
    5. public int read(byte b[], int off, int len) throws IOException {
    6. return readBytes(b, off, len);
    7. }
    8. }

    BufferedInputStream 源码精简,继承 InputStream 类,如下:

    1. public class BufferedInputStream extends InputStream {
    2. protected volatile InputStream in;
    3. public BufferedInputStream(InputStream in) {
    4. this.in = in;
    5. }
    6. public synchronized int read(byte b[], int off, int len) throws IOException {
    7. ...
    8. }
    9. }

    DataInputStream 源码精简,继承 InputStream 类,如下:

    1. // 具体的装饰类
    2. public class DataInputStream extends InputStream {
    3. protected volatile InputStream in;
    4. public DataInputStream(InputStream in) {
    5. this.in = in;
    6. }
    7. public final int read(byte b[], int off, int len) throws IOException {
    8. return in.read(b, off, len);
    9. }
    10. }

    组合各种类型的字节流,使用

    1. //读取文件 + 可缓存的字节流
    2. new BufferedInputStream(new FileInputStream("D:/c.txt"));
    3. //读取文件 + 直接读取基本类型的字节流
    4. new DataInputStream(new FileInputStream("D:/c.txt"));