设计模式之美
王争
前 Google 工程师,《数据结构与算法之美》专栏作者
120453 人已学习
新⼈⾸单¥98
登录后,你可以任选6讲全文学习
课程目录
已完结/共 113 讲
设计模式与范式:行为型 (18讲)
设计模式之美
15
15
1.0x
00:00/00:00
登录|注册

50 | 装饰器模式:通过剖析Java IO类库源码学习装饰器模式

上一节课我们学习了桥接模式,桥接模式有两种理解方式。第一种理解方式是“将抽象和实现解耦,让它们能独立开发”。这种理解方式比较特别,应用场景也不多。另一种理解方式更加简单,类似“组合优于继承”设计原则,这种理解方式更加通用,应用场景比较多。不管是哪种理解方式,它们的代码结构都是相同的,都是一种类之间的组合关系。
今天,我们通过剖析 Java IO 类的设计思想,再学习一种新的结构型模式,装饰器模式。它的代码结构跟桥接模式非常相似,不过,要解决的问题却大不相同。
话不多说,让我们正式开始今天的学习吧!

Java IO 类的“奇怪”用法

Java IO 类库非常庞大和复杂,有几十个类,负责 IO 数据的读取和写入。如果对 Java IO 类做一下分类,我们可以从下面两个维度将它划分为四类。具体如下所示:
针对不同的读取和写入场景,Java IO 又在这四个父类基础之上,扩展出了很多子类。具体如下所示:
在我初学 Java 的时候,曾经对 Java IO 的一些用法产生过很大疑惑,比如下面这样一段代码。我们打开文件 test.txt,从中读取数据。其中,InputStream 是一个抽象类,FileInputStream 是专门用来读取文件流的子类。BufferedInputStream 是一个支持带缓存功能的数据读取类,可以提高数据读取的效率。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《设计模式之美》
新⼈⾸单¥98
立即购买
登录 后留言

全部留言(127)

  • 最新
  • 精选
  • 万历十五年
    代理模式体现封装性,非业务功能与业务功能分开,而且使用是透明的,使用者只需要关注于自身的业务,在业务场景上适用于对某一类功能进行加强,比如日志,事务,权限。 装饰器模式体现多态性,优点在于避免了继承爆炸,适用于扩展多个平行功能。在场景上,这些扩展的功能可以像火车厢一样串起来,使原有的业务功能不断增强。 具体到“缓存”这个单个问题,两种模式都可以,主要还是看设计者的目的,如果是为了新增功能的隐藏性,就使用代理模式;如果设计者不仅要增加“缓存”功能,还要增加“过滤”等功能,就更适于装饰器模式。

    作者回复: 嗯嗯 ������

    4
    41
  • 汉江
    有个疑问 既然代码结构是一样的 那在于怎么叫了 我可以叫做代理模式 也可以叫做装饰器模式?

    作者回复: 主要看应用场景

  • Giacomo
    能不能把BufferedInputStream这些可以叠加的功能做成interface,然后定义一些内部的类

    作者回复: 你的意思是,定义很多不同的interface对吧。这样也可以,但用起来就没有现在的好用了。

    2
  • 永旭
    按照本节讲的内容, java io例子里 原始类: InputStream 共同父类: FileInputStream 装饰器类: BufferedInputStream , DataInputStream 是这关系吗 ? 因为InputStream本身是FileInputStream的父类 有点绕来着 .

    作者回复: 是的

  • 下雨天
    你是一个优秀的歌手,只会唱歌这一件事,不擅长找演唱机会,谈价钱,搭台,这些事情你可以找一个经纪人帮你搞定,经纪人帮你做好这些事情你就可以安稳的唱歌了,让经纪人做你不关心的事情这叫代理模式。 你老爱记错歌词,歌迷和媒体经常吐槽你没有认真对待演唱会,于是你想了一个办法,买个高端耳机,边唱边提醒你歌词,让你摆脱了忘歌词的诟病,高端耳机让你唱歌能力增强,提高了基础能力这叫装饰者模式。
    35
    560
  • 小晏子
    对于添加缓存这个应用场景使用哪种模式,要看设计者的意图,如果设计者不需要用户关注是否使用缓存功能,要隐藏实现细节,也就是说用户只能看到和使用代理类,那么就使用proxy模式;反之,如果设计者需要用户自己决定是否使用缓存的功能,需要用户自己新建原始对象并动态添加缓存功能,那么就使用decorator模式。
    10
    332
  • Jxin
    今天的课后题: 1.有意思,关于代理模式和装饰者模式,各自应用场景和区别刚好也想过。 1.代理模式和装饰者模式都是 代码增强这一件事的落地方案。前者个人认为偏重业务无关,高度抽象,和稳定性较高的场景(性能其实可以抛开不谈)。后者偏重业务相关,定制化诉求高,改动较频繁的场景。 2.缓存这件事一般都是高度抽象,全业务通用,基本不会改动的东西,所以一般也是采用代理模式,让业务开发从缓存代码的重复劳动中解放出来。但如果当前业务的缓存实现需要特殊化定制,需要揉入业务属性,那么就该采用装饰者模式。因为其定制性强,其他业务也用不着,而且业务是频繁变动的,所以改动的可能也大,相对于动代,装饰者在调整(修改和重组)代码这件事上显得更灵活。
    12
    154
  • 守拙
    补充关于Proxy Pattern 和Decorator Pattern的一点区别: Decorator关注为对象动态的添加功能, Proxy关注对象的信息隐藏及访问控制. Decorator体现多态性, Proxy体现封装性. reference: https://stackoverflow.com/questions/18618779/differences-between-proxy-and-decorator-pattern
    3
    98
  • andi轩
    对于为什么必须继承装饰器父类 FilterInputStream的思考: 装饰器如BufferedInputStream等,本身并不真正处理read()等方法,而是由构造函数传入的被装饰对象:InputStream(实际上是FileInputStream或者ByteArrayInputStream等对象)来完成的。 如果不重写默认的read()等方法,则无法完成如FileInputStream或者ByteArrayInputStream等对象所真正实现的read功能。 所以必须重写对应的方法,代理给这些被装饰对象进行处理(这也是类似于代理模式的地方)。 如果像DataInputStream和BufferedInputStream等每个装饰器都重写的这些方法话,会存在大量重复的代码。 所以让它们都继承FilterInputStream提供的默认实现,可以减少代码重复,让装饰器只聚焦在它自己的装饰功能上即可。
    9
    61
  • rammelzzz
    对于无需Override的方法也要重写的理解: 虽然本身BufferedInputStream也是一个InputStream,但是实际上它本身不作为任何io通道的输入流,而传递进来的委托对象InputStream才能真正从某个“文件”(广义的文件,磁盘、网络等)读取数据的输入流。因此必须默认进行委托。
    2
    35
收起评论
显示
设置
留言
99+
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部