66bdf2611c0f43e60b1537fbfbe69463
5分钟理解设计模式 —— 装饰器模式

[TOC]

概述:

  5分钟理解设计模式系列,将通过解决实际问题,来带您理解设计模式,本文希望带您搞懂的3个问题是:
1.什么是装饰器模式?
2.通过剖析Java IO类库,理解装饰器模式?
3.装饰器模式和代理模式的区别?

1.什么是装饰器模式?

装饰器模式(Decorator)主要用法是动态增强类的功能,就这一点而言,装饰器模式和代理模式较为相似。后面我们会对着重讨论这两种模式的异同。下面我们举一个生活中的例子:
一个蛋糕,我们为它添加水果,它就变成了水果蛋糕。
一个水果蛋糕,我们为它插上蜡烛,它就变成了生日蛋糕。
如果你对java io类库比较了解的话,可以知道,java io类库中大量运用了装饰器模式,接下来我们会通过阅读java io类库,来一起深入理解装饰器模式。

2.通过剖析Java IO类库,理解装饰器模式?

首先我们应该对java io类库有一个大致的了解。我们可以按照两个维度,将它分为4类。

而针对不同的使用场景,java io又在这四个父类之上,扩展出了很多子类。

我们来看一段读取数据的代码:

InputStream in = new FileInputStream("/test.txt");
InputStream bin = new BufferedInputStream(in);
byte[] data = new byte[128];
while (bin.read(data) != -1) {
  //省略具体操作
}

我们可以看到,java io使用的是一种组合的方式,来扩展fileInputStream的功能。那么使用继承可不可以呢?

组合 vs 继承

我们经常会听到这样一种说法,组合优于继承,在实际的开发过程中,这句话往往是没有问题的,我们很容易为了节省一部分代码,滥用继承,导致代码结构混乱。但是本着怀疑一切的思想,我们在这里还是来分析一下,为什么java io会选择组合,而不是继承。

为什么java io会选择组合,而不是继承。

我们可以看到,java io具有很多个子类,这些子类适用于不同的场景,如果使用继承的话,那么就需要派生大量的子类,如果是多种场景叠加的情况,那么类的继承关系将变得非常复杂,代码即不好扩展, 也不好维护。
其实通过继承来扩展功能也是可以的,比如tomcat的连接器。它将I/O模型和应用层协议,通过继承的方式进行了功能的组合。

这里我觉得一方面是I/O模型和应用层的关系相对固定,只能两两组合,另一方面,它们的数量也是可控的,所以这里tomcat使用了继承的方式来扩展功能。
通过上面的论述,我们知道了java io是通过组合的方式,来实现装饰器模式,从而实现一种功能的增强。

源码解析

public abstract class InputStream {
  //...
  public int read(byte b[]) throws IOException {
    return read(b, 0, b.length);
  }

  public int read(byte b[], int off, int len) throws IOException {
    //...
  }

  public long skip(long n) throws IOException {
    //...
  }

  public int available() throws IOException {
    return 0;
  }

  public void close() throws IOException {}

  public synchronized void mark(int readlimit) {}

  public synchronized void reset() throws IOException {
    throw new IOException("mark/reset not supported");
  }

  public boolean markSupported() {
    return false;
  }
}
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();
  }
}

FilterInputStream是一个java io抽象出来的装饰器父类,主要是用于实现一些InputStream中的默认实现,这样其他的装饰器类就可以专注于实现自己想要增强功能的方法。这种设计方法也被广泛运用在各种框架中。

3.装饰器模式和代理模式的区别?

它们的实现方式都是组合,主要区别是:
装饰器模式是对类原有功能的增强(读数据增强成加缓存,批量读数据)。
代理模式是对类原有功能的扩展(为执行方法增加记录日志的功能)。
最后,期待您的订阅和点赞,专栏每周都会更新,希望可以和您一起进步,同时也期待您的批评与指正!

© 著作权归作者所有
这个作品真棒,我要支持一下!
一个坚持原创的小专栏。分享编程知识,提升工作效率,致力于通过简单的语言,把编程这点事讲清楚。涵盖内容:java、设...
0条评论
top Created with Sketch.