结构型-Decorator

简介: 装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承。它主要的作用是给原始类添加增强功能。这也是判断是否该用装饰器模式的一个重要的依据。除此之外,装饰器模式还有一个特点,那就是可以对原始类嵌套使用多个装饰器。为了满足这个应用场景,在设计的时候,装饰器类需要跟原始类继承相同的抽象类或者接口。

装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承。它主要的作用是给原始类添加增强功能。这也是判断是否该用装饰器模式的一个重要的依据。除此之外,装饰器模式还有一个特点,那就是可以对原始类嵌套使用多个装饰器。为了满足这个应用场景,在设计的时候,装饰器类需要跟原始类继承相同的抽象类或者接口。


装饰器模式在Java I/O系统中的实现



从 Java IO 的设计来看,装饰器模式相对于简单的组合关系,还有两个比较特殊的地方。第一个比较特殊的地方是:装饰器类和原始类继承同样的父类,这样我们可以对原始类“嵌套”多个装饰器类。比如,下面这样一段代码,我们对 FileInputStream 嵌套了两个装饰器类:BufferedInputStream 和 DataInputStream,让它既支持缓存读取,又支持按照基本数据类型来读取数据。

InputStream in = new FileInputStream("/user/wangzheng/test.txt");
InputStream bin = new BufferedInputStream(in);
DataInputStream din = new DataInputStream(bin);
int data = din.readInt();


第二个比较特殊的地方是:装饰器类是对功能的增强,这也是装饰器模式应用场景的一个重要特点。实际上,符合“组合关系”这种代码结构的设计模式有很多,比如之前讲过的代理模式、桥接模式,还有现在的装饰器模式。尽管它们的代码结构很相似,但是每种设计模式的意图是不同的。就拿比较相似的代理模式和装饰器模式来说吧,代理模式中,代理类附加的是跟原始类无关的功能,而在装饰器模式中,装饰器类附加的是跟原始类相关的增强功能。

// 代理模式的代码结构(下面的接口也可以替换成抽象类)
public interface IA {
  void f();
}
public class A impelements IA {
  public void f() { //... }
}
public class AProxy implements IA {
  private IA a;
  public AProxy(IA a) {
    this.a = a;
  }
  public void f() {
    // 新添加的代理逻辑
    a.f();
    // 新添加的代理逻辑
  }
}
// 装饰器模式的代码结构(下面的接口也可以替换成抽象类)
public interface IA {
  void f();
}
public class A implements IA {
  public void f() { //... }
}
public class ADecorator implements IA {
  private IA a;
  public ADecorator(IA a) {
    this.a = a;
  }
  public void f() {
    // 功能增强代码
    a.f();
    // 功能增强代码
  }
}


实际上,如果去查看 JDK 的源码,你会发现,BufferedInputStream、DataInputStream 并非继承自 InputStream,而是另外一个叫 FilterInputStream 的类。那这又是出于什么样的设计意图,才引入这样一个类呢?


我们再重新来看一下 BufferedInputStream 类的代码。InputStream 是一个抽象类而非接口,而且它的大部分函数(比如 read()、available())都有默认实现,按理来说,我们只需要在 BufferedInputStream 类中重新实现那些需要增加缓存功能的函数就可以了,其他函数继承 InputStream 的默认实现。但实际上,这样做是行不通的。


对于即便是不需要增加缓存功能的函数来说,BufferedInputStream 还是必须把它重新实现一遍,简单包裹对 InputStream 对象的函数调用。具体的代码示例如下所示。如果不重新实现,那 BufferedInputStream 类就无法将最终读取数据的任务,委托给传递进来的 InputStream 对象来完成。这一部分稍微有点不好理解,你自己多思考一下。

public class BufferedInputStream extends InputStream {
  protected volatile InputStream in;
  protected BufferedInputStream(InputStream in) {
    this.in = in;
  }
  // f()函数不需要增强,只是重新调用一下InputStream in对象的f()
  public void f() {
    in.f();
  }  
}


实际上,DataInputStream 也存在跟 BufferedInputStream 同样的问题。为了避免代码重复,Java IO 抽象出了一个装饰器父类 FilterInputStream,代码实现如下所示。


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

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


总结



意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。


优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。


缺点:多层装饰比较复杂。


使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。


注意事项:可代替继


参考


设计模式之美设计模式代码重构-极客时间


https://time.geekbang.org/column/intro/250


装饰器模式 | 菜鸟教程


https://www.runoob.com/design-pattern/decorator-pattern.html


目录
相关文章
|
6月前
|
设计模式
设计模式之装饰器 Decorator
设计模式之装饰器 Decorator
44 1
|
设计模式 Java
Java设计模式-装饰器模式(Decorator)
Java设计模式-装饰器模式(Decorator)
结构型模式 - 装饰器模式(Decorator Pattern)
结构型模式 - 装饰器模式(Decorator Pattern)
|
设计模式 自动驾驶
装饰器模式Decorator
煎饼果子装饰器&汽车装饰器
583 3
装饰器模式Decorator
|
设计模式
装饰模式(Decorator)
装饰模式(Decorator)
|
设计模式 缓存 Java
设计模式-Adapter适配器模式和Decorator装饰者模式
设计模式-Adapter适配器模式和Decorator装饰者模式
设计模式-Adapter适配器模式和Decorator装饰者模式
|
设计模式 uml
设计模式——装饰模式(Decorator)
设计模式——装饰模式(Decorator)
150 0
设计模式——装饰模式(Decorator)
|
设计模式 Java 数据库连接
【Java设计模式系列】装饰器模式(Decorator Pattern)(下)
【Java设计模式系列】装饰器模式(Decorator Pattern)
127 0
【Java设计模式系列】装饰器模式(Decorator Pattern)(下)
|
设计模式 Java
【Java设计模式系列】装饰器模式(Decorator Pattern)(上)
【Java设计模式系列】装饰器模式(Decorator Pattern)
138 0
【Java设计模式系列】装饰器模式(Decorator Pattern)(上)
|
设计模式 Java
浅谈JAVA设计模式之——装饰模式(Decorator)
动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。
155 0
浅谈JAVA设计模式之——装饰模式(Decorator)