news 2026/4/22 10:45:09

Java设计模式——装饰器模式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java设计模式——装饰器模式

当我们想对一个类进行功能扩展的时候,最简单的方法就是继承该类然后进行修改,但是一个接口下的实现类很多,每个子类都进行继承扩展的话又会诞生很多子类,造成类爆炸的情况。

装饰器模式属于结构型设计模式,就可以做到在不改变原代码的情况下完成功能的扩展,符合OCP原则,也避免了类爆炸的情况。

Java中的IO流大量使用了装饰器模式,可以参考官方的源码进行学习。

  • 在装饰器设计中,两个重要角色:装饰者与被装饰者

  • 装饰器设计模式中,要求装饰者与被装饰者应实现同一个接口/同一些接口/继承同一个抽象类

  • 因为实现同一个接口以后,对于客户端程序来说,使用装饰者时就像在使用被装饰者一样

  • 装饰者中含有被装饰者的引用(A has a B),尽量使用has a[耦合度低一些],不要使用is a(即组合优于继承原则)

例如我们想实现一个自己的Set,能够记录下被移除的元素,有些人可能会想到继承个set,但是我们需要注意,继承只能单继承,这样操作会使该类失去其他继承的资格,所以优先还是实现set接口而不影响原本的继承扩展。

-HistorySet.java

packageinsight;importjava.util.*;/** * @author: wangcai * @date: 2025/12/15 */publicclassHistorySet<E>implementsSet<E>{/* 记录Remove掉的元素 */List<E>removeList=newArrayList<>();finalSet<E>delegate;publicHistorySet(Set<E>delegate){this.delegate=delegate;}@Overridepublicintsize(){returndelegate.size();}@OverridepublicbooleanisEmpty(){returndelegate.isEmpty();}@Overridepublicbooleancontains(Objecto){returndelegate.contains(o);}@OverridepublicIteratoriterator(){returndelegate.iterator();}@OverridepublicObject[]toArray(){returnnewObject[0];}@Overridepublicbooleanadd(Objecto){returndelegate.add((E)o);}@Overridepublicbooleanremove(Objecto){booleanremove=delegate.remove(o);if(remove){removeList.add((E)o);}returnremove;}@OverridepublicbooleanaddAll(Collectionc){returnfalse;}@Overridepublicvoidclear(){}@OverridepublicbooleanremoveAll(Collectionc){returnfalse;}@OverridepublicbooleanretainAll(Collectionc){returnfalse;}@OverridepublicbooleancontainsAll(Collectionc){returnfalse;}@OverridepublicObject[]toArray(Object[]a){returnnewObject[0];}@OverridepublicStringtoString(){return"HistorySet{ "+"delegate = "+delegate.toString()+" "+"removeList = "+removeList.toString()+" }";}}

例如上面这样,这样可以自己无需实现set的功能就可以具备set的能力。而且支持手动传入set实例,由用户自己控制想要扩展的set,HashSet或者TreeSet。而如果通过继承实现该扩展功能的话,就不能这么灵活,我们需要每种实现都单独编写一个扩展类。

-Usage.java

packageinsight;importjava.util.HashSet;/** * @author: wangcai * @date: 2025/12/15 */publicclassUsage{publicstaticvoidmain(String[]args){HistorySet<Integer>integers=newHistorySet<Integer>(newHashSet<>());integers.add(1);integers.add(2);integers.add(3);integers.add(4);integers.add(5);integers.remove(2);integers.remove(2);integers.remove(5);System.out.println(integers);}}/* HistorySet{ delegate = [1, 3, 4] removeList = [2, 5] } */

并且我们可以反复的包,因为我们实现的本身也是一个set集合,还是能作为构造器参数传入。

packageinsight;importjava.util.HashSet;/** * @author: wangcai * @date: 2025/12/15 */publicclassUsage{publicstaticvoidmain(String[]args){HistorySet<Integer>integers=newHistorySet<>(newHashSet<>());HistorySet<Integer>integers1=newHistorySet<>(integers);integers1.add(1);integers1.add(2);integers1.add(3);integers1.add(4);integers1.add(5);integers1.remove(2);integers1.remove(2);integers1.remove(5);System.out.println(integers1);}}/* HistorySet{ delegate = HistorySet{ delegate = [1, 3, 4] removeList = [2, 5] } removeList = [2, 5] } */

我们可以看一下Java中的一些实现:

Collections.synchronizedCollection()

publicstatic<T>Collection<T>synchronizedCollection(Collection<T>c){returnnewSynchronizedCollection<>(c);}

===BufferedInputStream

我们自己手写一个BufferedInputStream模仿Java IO流中的缓冲输入流来提升读取效率。

-Usage.java

packageinsight.input;importjava.io.File;importjava.io.FileInputStream;importjava.time.Instant;/** * @author: wangcai * @date: 2025/12/16 */publicclassUsage{publicstaticvoidmain(String[]args)throwsException{Filefile=newFile("src/main/resources/深入理解Java虚拟机(第3版).pdf");longmillisecond=Instant.now().toEpochMilli();try(FileInputStreamfileInputStream=newFileInputStream(file)){while(true){intread=fileInputStream.read();if(read==-1){break;}}}System.out.println("耗时: "+(Instant.now().toEpochMilli()-millisecond)+"ms");}}/* 耗时: 62638ms */

可以看的出来,耗时很久,基于此我们利用装饰器模式自己改造一下FileInputStream。缓冲区的核心在于我们需要读取的时候保证效率高,但是又不需要一口气读完,所以我们需要创建一个缓冲区读取文件了保存下缓存内容,需要的时候在内存中直接拿取。

-BufferedFileInputStream.java

packageinsight.input;importjava.io.IOException;importjava.io.InputStream;/** * @author: wangcai * @date: 2025/12/16 */publicclassBufferedFileInputStreamextendsInputStream{privatefinalInputStreamin;// 缓冲区 默认8kbbyte[]buffer=newbyte[8192];// 标志位 为-1时代表不可读 其余为读取的开始位置privateintbufferPos=-1;// 用来避免超出的读取文件privateintcapacity=-1;publicBufferedFileInputStream(InputStreaminputStream){this.in=inputStream;}@Overridepublicintread()throwsIOException{if(buffCanRead()){returnreadFromBuffer();}refreshBuffer();if(!buffCanRead()){return-1;}returnreadFromBuffer();}privateintreadFromBuffer(){returnbuffer[bufferPos++]&0xff;}privatevoidrefreshBuffer()throwsIOException{capacity=this.in.read(buffer);bufferPos=0;}privatebooleanbuffCanRead(){if(capacity==-1){returnfalse;}returnbufferPos!=capacity;}@Overridepublicvoidclose()throwsIOException{super.close();}}
packageinsight.input;importjava.io.File;importjava.io.FileInputStream;importjava.io.InputStream;importjava.time.Instant;/** * @author: wangcai * @date: 2025/12/16 */publicclassUsage{publicstaticvoidmain(String[]args)throwsException{Filefile=newFile("src/main/resources/深入理解Java虚拟机(第3版).pdf");longmillisecond=Instant.now().toEpochMilli();try(InputStreamfileInputStream=newBufferedFileInputStream(newFileInputStream(file))){while(true){intread=fileInputStream.read();if(read==-1){break;}}}System.out.println("耗时: "+(Instant.now().toEpochMilli()-millisecond)+"ms");}}/* 耗时: 49ms */

效率大大提升,这也是官方源码中类似的用法的实践。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/20 19:11:55

免费无广!燃脂腹肌速成 APP,宅家就能练出线条

不想去健身房人挤人&#xff0c;也懒得户外吹风&#xff0c;却想练出紧致线条的小伙伴&#xff0c;这款 APP 直接封神&#xff01; 全程无广无弹窗&#xff0c;打开不用看广告、不用注册登录&#xff0c;纯粹到让人惊喜 —— 核心功能完全免费&#xff0c;对学生党、懒人党太友…

作者头像 李华
网站建设 2026/4/22 23:11:22

Vue 官方中文文档全解析:从零开始掌握现代前端开发

Vue 官方中文文档全解析&#xff1a;从零开始掌握现代前端开发 【免费下载链接】docs-zh-cn Vue 文档官方中文翻译 &#xff5c; Official Chinese translation for Vue docs 项目地址: https://gitcode.com/gh_mirrors/do/docs-zh-cn 作为全球最受欢迎的前端框架之一&a…

作者头像 李华
网站建设 2026/4/18 6:37:47

量子计算结果不稳定?你必须知道的VSCode+Jupyter 7个调试秘籍

第一章&#xff1a;量子计算结果不稳定&#xff1f;根源剖析与VSCodeJupyter调试必要性量子计算在理论上具备远超经典计算的潜力&#xff0c;但在实际开发过程中&#xff0c;开发者常遭遇“结果不稳定”的问题——相同电路在多次运行中输出不一致。这一现象并非硬件故障&#x…

作者头像 李华
网站建设 2026/4/22 21:55:16

终极指南:用Oni-Duplicity轻松定制《缺氧》游戏存档

终极指南&#xff1a;用Oni-Duplicity轻松定制《缺氧》游戏存档 【免费下载链接】oni-duplicity A web-hosted, locally-running save editor for Oxygen Not Included. 项目地址: https://gitcode.com/gh_mirrors/on/oni-duplicity Oni-Duplicity是一款专为《缺氧》&am…

作者头像 李华
网站建设 2026/4/20 2:39:10

浏览器水印终极解决方案:watermark-js-plus实战指南

浏览器水印终极解决方案&#xff1a;watermark-js-plus实战指南 【免费下载链接】watermark-js-plus watermark for the browser 项目地址: https://gitcode.com/gh_mirrors/wa/watermark-js-plus 在日常Web开发中&#xff0c;你是否曾遇到这样的困境&#xff1a;重要文…

作者头像 李华
网站建设 2026/4/19 5:51:01

深度解析Q#与Python变量共享机制(仅限高级开发者阅读)

第一章&#xff1a;Q#-Python 的变量同步在混合量子-经典计算编程中&#xff0c;Q# 与 Python 的协同工作已成为开发量子算法的重要模式。尽管 Q# 负责实现核心量子操作&#xff0c;Python 常用于控制流程、数据预处理和结果分析。因此&#xff0c;实现两者之间的变量同步至关重…

作者头像 李华