请先关注 [低调大师] 公众号 优秀的自媒体个人博客,低调大师,许军

低调大师

您现在的位置是:首页>文章详情

文章详情

面向对象编程内功心法系列十一(聊一聊装饰器模式)

2021-03-14 31热度

1.引子

下午好!今天我要分享给你的是我们面向对象编程内功心法系列的第十一篇文章,在这一篇我们一起来聊一聊装饰器设计模式

说起装饰器设计模式,你不应该感到陌生。我们日常开发中,经常有网络操作、文件操作方面的需求,比如说读写网络数据包,比如说读写文件。你一定已经想到了,针对刚才提出的需求,jdk给我们提供了丰富的IO工具类库。比如说

  • 字节流:InputStream、OutputStream、BufferedInputStream、BufferedOutputStream

  • 字符流:Reader、Writer、BufferedReader、BufferedWriter

不知你平常是否有阅读源码的习惯,如果我们打开IO类库源码,我们会发现jdk整个IO类库的设计实现,就应用了装饰器设计模式。本次分享文章的最后部分,我将带着你一起来看一看。

2.案例

2.1.什么是装饰器设计模式

根据设计模式的分类,装饰器设计模式属于结构型设计模式,通过组合关系将对象组装在一起,实现组合对象间功能的增强

看到增强,与结构型设计模式字眼,你是否会想到我们上一篇文章分享的代理设计模式。我们说代理设计模式其中的作用之一就是增强,都是增强,那么代理设计模式与装饰器设计模式有什么区别吗?

事实上,如果单纯从代码层面进行比较的话,装饰器设计模式的代码实现(参考稍后案例代码),与代理设计模式代码实现(最佳参考静态代理代码实现)。我们几乎看不出来有什么差异,那你可能会存有疑问了:该如何区分呢?

这也是这一篇文章我想要分享给你的重要关注点之一。很多设计模式,我们单纯从代码实现层面难以看出来差异,尤其结构型设计模式。那我们如何区分呢?我们说每一种设计模式,都是前辈们针对不同的问题域,经过长期的良好经验实践,最终总结出来的。于是,我们可以得出一个良好实践经验:对于不同的设计模式,我们把关注点放在该设计模式解决的问题域上来,就比较好区分了

其它的设计模式我们暂时放一放,今天我们来看一看装饰器设计模式,与代理设计模式之间的区别

  • 作用上,我们说代理设计模式有两个作用:一是增强;而是访问控制(安全作用,还记得经纪人,与歌星的关系吗)

  • 作用上,我们说装饰器设计模式仅有一个作用:就是增强

  • 具体到增强作用上,我举一个例子,你应该就能明白了

    • 代理模式增强:歌星只关注唱歌,其它的事情比如商务合同签订,出场安排、行程安排都交给经纪人。你看这就是代理增强,属于增强目标对象(歌星)唱歌以外额外的能力

    • 装饰器模式增强:歌星唱歌,老是忘词,遭到歌迷吐槽没有认真对待演唱会,于是该歌星想了个办法,买一个高端耳机,一遍演唱一边通过高端耳机提醒歌词。你看这就是装饰器增强,属于基础能力(唱歌)的增强

2.2.装饰器设计模式案例

2.2.1.爱忘词的歌星

/** * 装饰器设计模式案例:歌星 * * @author ThinkPad * @version 1.0 * @date 2021/3/14 18:53 */ public class Singer { /** * 歌星唱歌 */ public void song(){ System.out.println("Singer:唱歌老是忘词,怎么办呢?"); } }

2.2.2.戴着高端耳机的歌星

/** * 装饰器设计模式案例:戴了高端耳机的歌星 * * @author ThinkPad * @version 1.0 * @date 2021/3/14 18:55 */ public class EarPhoneSinger extends Singer{ //关注点一:组合关系持有Singer实例 protected Singer singer; public EarPhoneSinger(Singer singer){ this.singer = singer; } /** * 歌星戴着高端耳机唱歌 */ @Override public void song(){ // 关注点二:调用组合对象方法 this.singer.song(); // 关注点三:增强 System.out.println("EarPhoneSinger:买一个高端耳机,一边唱歌一边提醒歌词,真好!"); } }

2.2.3.演唱会

public static void main(String[] args) { // 创建原生对象:歌星 Singer singer = new Singer(); // 创建装饰器对象:买了高端耳机的歌星 Singer earPhoneSinger = new EarPhoneSinger(singer); // 戴着高端耳机唱歌 earPhoneSinger.song(); } #执行结果 Singer:唱歌老是忘词,怎么办呢? EarPhoneSinger:买一个高端耳机,一边唱歌一边提醒歌词,真好! Process finished with exit code 0

通过歌星演唱会上,戴着高端耳机提醒歌词唱歌,我给你演示了一个装饰器设计模式的代码实现案例。不知你有没有发现?从代码实现层面看,与代理设计模式代码实现结构是一样的对吧。

这里,我还是要强调一下,并希望你能留意:理解设计模式,我们应该从应用场景出发,从该设计模式解决的问题域出发去关注一个设计模式。而不是单纯从代码实现上去关注它,这是我个人理解设计模式的一个良好实践,在这里将它分享给你

2.3.分析jdk的IO类库中装饰器设计模式的应用

理解了装饰器设计模式,我们再来看一看jdk的IO类库设计中,对装饰器设计模式的应用。我将带着你一起来看一看典型的jdk IO类库的部分源码,我将选取字节输入流作为案例,其它比如说字符流、字节输出流,期望并建议你一定要去看一看。

源码分析中,我将贴出关键源码,并且增加注释帮助你去理解,那就让我们开始吧。看代码前先来看一下类图:

 

2.3.1.字节流InputStream

使用字节输入流,主要用途是读取网络数据包,或者读取文件内容。因此我们主要关注类的结构,和read方法

/** *InputStream类:提供了基础读取方法read(byte b[], int off, int len) */ public abstract class InputStream implements Closeable { // 省略其它代码...... // read方法:一个字节一个字节读取 //参数:b[]存放数据字节数组、off读取偏移量、len读取多少个字节 public int read(byte b[], int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int c = read(); if (c == -1) { return -1; } b[off] = (byte)c; int i = 1; try { for (; i < len ; i++) { c = read(); if (c == -1) { break; } b[off + i] = (byte)c; } } catch (IOException ee) { } return i; } // 省略其它代码...... }

2.3.2.过滤字节流FilterInputStream

/** *FilterInputStream类:继承于InputStream,在读取字节基础能力上,【增强】提供了过滤能力 */ public class FilterInputStream extends InputStream { // 关注点一:组合关系持有InputStream实例 /** * The input stream to be filtered. */ protected volatile InputStream in; // 关注点二:read方法内,通过持有的InputStream实例进行数据读取 public int read(byte b[], int off, int len) throws IOException { return in.read(b, off, len); } // 省略其它代码...... }

2.3.3.缓冲字节流BufferedInputStream

/** *BufferedInputStream类:继承于FilterInputStream,相当于扩展InputStream *在读取字节基础能力+过滤能力上,【增强】提供了缓冲能力,性能效率更高效 */ public class BufferedInputStream extends FilterInputStream { // 省略其它代码...... // 关注点一:【增强】提供缓冲能力, //注意代码行:int nread = read1(b, off + n, len - n); public synchronized int read(byte b[], int off, int len) throws IOException { getBufIfOpen(); // Check for closed stream if ((off | len | (off + len) | (b.length - (off + len))) < 0) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int n = 0; for (;;) { // 调用read1方法读取数据 int nread = read1(b, off + n, len - n); if (nread <= 0) return (n == 0) ? nread : n; n += nread; if (n >= len) return n; // if not closed but no bytes available, return InputStream input = in; if (input != null && input.available() <= 0) return n; } } // 关注点二:调用被包装的InputStream实例方法 // 注意代码行:return getInIfOpen().read(b, off, len); private int read1(byte[] b, int off, int len) throws IOException { int avail = count - pos; if (avail <= 0) { if (len >= getBufIfOpen().length && markpos < 0) { // 调用被包装的InputStream实例方法 return getInIfOpen().read(b, off, len); } fill(); avail = count - pos; if (avail <= 0) return -1; } int cnt = (avail < len) ? avail : len; System.arraycopy(getBufIfOpen(), pos, b, off, cnt); pos += cnt; return cnt; } // 省略其它代码...... }

 

收藏 (0)

相关文章

    文章评论

    共有0条评论来说两句吧...