设计模式学习(九):装饰器模式


设计模式学习(九):装饰器模式

作者:Grey

原文地址:

CSDN:设计模式学习(九):装饰器模式

装饰器模式

装饰器模式是一种结构型模式。

顾名思义,就是对某个方法或者对象进行装饰,举个简单的例子,有个圆形类 Circle,我需要把这个圆形的涂上红色,其实就是新增一个装饰器来装饰这个圆形类。

如果要让装饰器通用一些,可以处理圆形类对应的抽象类 Sharp ,那么对于任意 Sharp 的子类,都可以用红色装饰器来涂红色。

示例代码如下

我们先定义 Sharp 这个抽象类

public abstract class Sharp {
    protected abstract void draw();
}

然后我们定义 Sharp 的装饰类 SharpDecorator ,这个类是所有装饰器类的抽象类,后续的装饰器只需要实现这个抽象类就可以对 Sharp 进行各种装饰了,

public abstract class SharpDecorator extends Sharp {
    protected Sharp decoratedSharp;

    public SharpDecorator(Sharp decoratedSharp) {
        this.decoratedSharp = decoratedSharp;
    }
}

红色装饰器实现这个抽象类即可:

public class RedSharpDecorator extends SharpDecorator {
    public RedSharpDecorator(Sharp decoratedSharp) {
        super(decoratedSharp);
    }

    private static void redIt() {
        System.out.println("[RED]");
    }

    @Override
    protected void draw() {
        redIt();
        this.decoratedSharp.draw();
        redIt();
    }
}

主方法调用的时候只需要:

new RedSharpDecorator(new Circle()).draw();

UML 图如下:

image

说明:

  1. 装饰器类和原始类继承同样的父类,这样我们可以对原始类“嵌套”多个装饰器类。

  2. 装饰器类是对功能的增强,这也是装饰器模式应用场景的一个重要特点。符合“组合关系”这种代码结构的设计模式有很多,比如、,还有现在的装饰器模式。尽管它们的代码结构很相似,但是每种设计模式的意图是不同的。就拿比较相似的代理模式和装饰器模式来说:

代理模式中,代理类附加的是跟原始类无关的功能;

装饰器模式中,装饰器类附加的是跟原始类相关的增强功能。

装饰器模式应用

在 JDK 中,BufferedInputStream 、 DataInputStream 并非继承自 InputStream ,而是另外一个叫 FilterInputStream 的类。

这是因为 InputStream 是一个抽象类而非接口,而且它的大部分函数(比如 read()available())都有默认实现,按理来说,我们只需要在 BufferedInputStream 类中重新实现那些需要增加缓存功能的函数就可以了,其他函数只需要复用 InputStream 的默认实现。但实际上,这样做是行不通的。对于即便是不需要增加缓存功能的函数来说,BufferedInputStream 还是必须把它重新实现一遍,简单包裹对 InputStream 对象的函数调用。那 BufferedInputStream 类就无法将最终读取数据的任务,委托给传递进来的 InputStream 对象来完成,DataInputStream 也存在跟 BufferedInputStream 同样的问题。为了避免代码重复,Java I/O 包中抽象出了一个装饰器父类 FilterInputStream,包装了 InputStream 的默认实现

package java.io;

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);
    }

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

    public void close() throws IOException {
        in.close();
    }

    public synchronized void mark(int readlimit) {
        in.mark(readlimit);
    }

    public synchronized void reset() throws IOException {
        in.reset();
    }

    public boolean markSupported() {
        return in.markSupported();
    }
}

InputStream 的所有的装饰器类( BufferedInputStream 和 DataInputStream )都继承自这个装饰器父类。这样,装饰器类只需要实现它需要增强的方法就可以了,其他方法继承装饰器父类的默认实现。

其他应用

  • Java 中的 I/O 流, Read/InputStream ,Write/OutputStream

  • Java 中的 UnmodifiableCollection

  • Spring 中的 HttpHeadResponseDecorator, 还有对 Cache 的装饰类 TransactionAwareCacheDecorator

UML 和 代码

UML 图

代码

更多

设计模式学习专栏

参考资料

  • Effective Java中文版(第3版)

  • Head First 设计模式

  • 设计模式-可复用面向对象软件的基础

  • 坦克大战-马士兵

  • 菜鸟教程-设计模式

  • 极客时间-设计模式之美-王争