装饰器模式与开闭原则

发布时间:2026/10/23 10:30:07

装饰器模式与开闭原则 本系列将持续更新「设计模式」相关内容 从核心设计原则到设计模式帮你真正用起来 觉得有帮助欢迎点个关注 一起交流学习共同进步前文本章可以称为“为爱用继承的家伙打开设计之眼”。我们即将再度探讨典型的继承滥用问题。你将学到如何在运行时使用对象组合的形式装饰你的类。为什么一旦知道装饰的技巧你将能给你的或别人的对象一个新的责任而不需要对底层类的代码做任何改变。本文的灵感与知识骨架主要来自神书《Head First 设计模式》中的装饰者模式章节。为了更易读笔者用自己的“文件读取与后缀装饰”案例进行了重构并加入了个人理解。水平有限如有错漏欢迎指正~本文讲解逻辑简单继承实现 → 发现问题 → 引入开闭原则 → 装饰器模式。1.一个简单的文件读取程序1.1 程序介绍假设公司要求我们开发一个文件读取的程序该程序可以读取数字字母还有特殊符号三个功能基础类图如下:DataReaderread() : StringNumberReaderread() : StringLetterReaderread() : StringSpecialCharReaderread() : String1.2新的需求现在老板需要我们给读取到的数据都增加一个后缀.data。2. 第一种实现方式有误导2.1使用继承来复用代码使用继承的复用功能我们可以在父类的读取方法中实现后缀data功能然后子类的方法中在自己的逻辑基础上调用父类的读取方法即可代码如下:abstractclassDataReader{publicStringread(){return.data;}}classNumberReaderextendsDataReader{OverridepublicStringread(){return12345super.read();}}classLetterReaderextendsDataReader{OverridepublicStringread(){returnabcdesuper.read();}}classSpecialCharReaderextendsDataReader{OverridepublicStringread(){return!#$%super.read();}}测试一下:publicclassClient{publicstaticvoidmain(String[]args){DataReaderreadernewNumberReader();System.out.println(reader.read());// 12345.datareadernewLetterReader();System.out.println(reader.read());// abcde.datareadernewSpecialCharReader();System.out.println(reader.read());// !#$%.data}}2.2又有新的需求上面的代码运行得似乎很完美但现在老板需要我们给除了数字之外的数据都添加上后缀 NotNumber。为了让两个子类复用同一段后缀逻辑我们很自然地想到把它放进父类的 read() 里代码如下abstractclassDataReader{publicStringread(){return.dataNotNumber;}}此时继承复用的问题就暴露了1.为了复用只能改父类。想让 LetterReader 和 SpecialCharReader 共享 NotNumber 逻辑就得动 DataReader若不想动父类就只能在这两个子类里各编码 .data 和 NotNumber重复且难维护——两条路都不好走。2.数字被误伤。NumberReader 通过 super.read() 被动继承了父类的返回值明明不在需求范围内却被加上了 NotNumber。3.继承是编译期绑定。.data、NotNumber 都写死在 read() 的返回值里运行时既无法切换后缀也无法决定这个对象要不要加更无法叠加多种后缀。3.开闭原则3.1为什么要开闭原则分析2.2的例子我们可以发现一个类的职责功能是会变化的所以我们应该对外提供扩展的功能同时不能修改原有的代码例子2.2中当增加扩展功能的时候我们不得不修改原有父类代码同时子类中的逻辑也会受到改变并且数字类还出错了这就是为什么需要对修改关闭~3.2开闭原则定义软件实体应当对扩展开放但对修改关闭。4.装饰器模式4.1装饰器模式原理定义和类图有个模式是比较符合开闭原则的就是装饰器模式我们来看看它的原理:装饰器模式的本质是动态地给一个对象添加一些额外的职责。它通过组合的方式把核心功能和附加功能一层层包裹起来形成类似“洋葱”或“俄罗斯套娃”的结构。定义:动态地给一个对象添加一些额外的职责。就增加功能而言装饰者模式比生成子类更为灵活。类图:4.2让我们的程序使用装饰器模式类图:«abstract»DataReaderread() : StringNumberReaderread() : StringLetterReaderread() : StringSpecialCharReaderread() : String«abstract»DataReaderDecorator#DataReader readerDataReaderDecorator(DataReader reader)read() : StringDataSuffixDecoratorDataSuffixDecorator(DataReader reader)read() : StringNotNumberDecoratorNotNumberDecorator(DataReader reader)read() : String代码:// 抽象构件只负责定义读取行为不再承载任何后缀逻辑abstractclassDataReader{publicabstractStringread();}// 具体构件各自只关心自己的核心数据classNumberReaderextendsDataReader{OverridepublicStringread(){return12345;}}classLetterReaderextendsDataReader{OverridepublicStringread(){returnabcde;}}classSpecialCharReaderextendsDataReader{OverridepublicStringread(){return!#$%;}}// 抽象装饰者持有被装饰对象并保持与构件相同的接口abstractclassDataReaderDecoratorextendsDataReader{protectedDataReaderreader;publicDataReaderDecorator(DataReaderreader){this.readerreader;}OverridepublicStringread(){returnreader.read();}}// 具体装饰者追加 .data 后缀classDataSuffixDecoratorextendsDataReaderDecorator{publicDataSuffixDecorator(DataReaderreader){super(reader);}OverridepublicStringread(){returnreader.read().data;}}// 具体装饰者追加 NotNumber 后缀classNotNumberDecoratorextendsDataReaderDecorator{publicNotNumberDecorator(DataReaderreader){super(reader);}OverridepublicStringread(){returnreader.read()NotNumber;}}代码测试:publicclassClient{publicstaticvoidmain(String[]args){// 1. 老板的需求一所有数据都加 .dataDataReadernumbernewDataSuffixDecorator(newNumberReader());System.out.println(number.read());// 12345.data// 2. 老板的需求二数字之外的再加 NotNumberDataReaderletternewNotNumberDecorator(newDataSuffixDecorator(newLetterReader()));System.out.println(letter.read());// abcde.dataNotNumberDataReaderspecialnewNotNumberDecorator(newDataSuffixDecorator(newSpecialCharReader()));System.out.println(special.read());// !#$%.dataNotNumber// 3. 数字不在 NotNumber 的需求范围内就不加这层装饰按需组合// 此处 number 已经是 12345.data没有被 误伤// 4. 想调整顺序装饰顺序由客户端自由决定DataReaderanothernewDataSuffixDecorator(newNotNumberDecorator(newLetterReader()));System.out.println(another.read());// abcdeNotNumber.data// 5. 想不加任何后缀也行直接用具体构件DataReaderrawnewLetterReader();System.out.println(raw.read());// abcde// 6. 甚至可以把两个装饰者同时叠加到同一个对象上DataReadertwicenewDataSuffixDecorator(newDataSuffixDecorator(newNumberReader()));System.out.println(twice.read());// 12345.data.data}}
延伸阅读

更多相关文章

2026/10/23 10:30:07

【无标题】第三章

一、存储系统概述 & 半导体存储器(第一部分) 3.1 存储系统概述 存储系统的层次结构 计算机存储采用分层结构,速度越快,容量越小,价格越高;从CPU向外依次:寄存器 → Cache → 主存 → 辅存。…

2026/10/23 10:25:06

基于k-means算法的中老年高血压人群的分析系统的设计与实现|大数据专业项目|毕设项目|大数据专业设计|毕设

一、項目介绍 摘 要 本系统基于k-means算法,针对中老年高血压人群进行了深入的分析与设计。系统采用了先进的技术框架,后端使用Django框架负责数据管理和服务端逻辑,前端则采用Vue框架构建用户界面,实现了前后端分离的高效开发模…

2026/10/23 11:30:39

计算机考研大纲深度拆解:从考纲到复习地图的实战指南

1. 一份考纲PDF背后的信息差每年九月前后,计算机专业的备考圈子里总会流传一份文件,文件名通常长这样:2019年计算机考研大纲(原文).pdf。很多人第一次拿到它,第一反应是“就这?”——薄薄几十页,排版朴素&a…

2026/10/23 11:30:39

SpringBoot 连接带密码 Access 数据库:UCanAccess 实战与避坑指南

1. 为什么还要折腾 SpringBoot 连 Access先把结论摆在前面:SpringBoot 连 Access 这件事,在今天的生产环境里绝对不是主流选择,但它真实存在,而且需求比很多人想象的要顽固。我最近就碰到一个场景,某公司内部有一套跑了…

2026/10/23 11:30:39

C语言模拟操作系统:从时间片轮到LRU,一份可运行的课设全解析

简介:东北大学操作系统课程设计完整实现,围绕进程管理、内存管理、文件系统与设备管理四大核心模块展开,适合计算机专业学生作为课设参考,也可用于操作系统原理的实践复习。资源共24个文件,主体为21个C源文件&#xff…

2026/10/23 11:30:39

iptables.service找不到?深入解析Linux防火墙管理的范式迁移

1. 项目概述:为什么一条报错提示能暴露系统管理的底层逻辑“Redirecting to /bin/systemctl status iptables.service Unit iptables.service could not be found”——这行看似简单的终端输出,其实是Linux系统演进过程中一个极具标志性的“时代断层线”…

2026/10/23 11:30:39

Lenovo Vantage 4.10.371.0离线安装与排错指南

简介:联想Vantage V4.10.371.0 系统之家整合包面向使用联想电脑、希望集中完成驱动更新与设备维护的用户,提供硬件检测、系统优化、安全防护与驱动管理等常用入口,适合想快速部署官方管理工具、减少逐项查找驱动成本的日常使用场景。压缩包共…

2026/10/23 11:25:36

Excel按行拆分实战:openpyxl流式脚本与数据防失真

1. 需求场景拆解:什么情况下非要把 Excel 切开1.1 四种高频切分场景,看看你中了几个Excel 按行切分这件事听着像小需求,但只要你在数据岗待过一段时间,就会发现它几乎是每个月底的固定动作。我把过去几年遇到过的情况归了归类&…

2026/10/22 11:29:28

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/23 0:02:26

Verilog实现FPGA密码锁:矩阵键盘与状态机设计实战

说实话,这几年接触了不少想入门FPGA的朋友,如果你问哪个项目能一口气把Verilog的核心基本功串起来,我通常会推荐电子密码锁。这个项目看着不起眼,却把矩阵键盘扫描、按键消抖、状态机设计、动态显示刷新、外设控制这些数字逻辑里的…

2026/10/23 0:02:26

数据服务监控运维实战:从指标体系到告警治理与故障排查

周末凌晨两点,手机震了。第一反应是“数据服务挂了”。打开电脑看了下,果然,某张核心报表的数据产出时间已经超过SLA两小时,下游三个业务方连着发来问询。这种场景,做过大数据运维的人都不会陌生。数据服务的监控与运维…

2026/10/23 0:02:26

FreeRTOS任务通知实战详解:内核原理、API与避坑指南

裸机编程切到RTOS之后,任务之间的通信和同步永远是躲不开的坎。前面聊过队列、信号量、互斥锁、事件组,这次把FreeRTOS任务通知(Task Notification)单独拎出来讲透。原因很简单:任务通知是FreeRTOS里最容易用错、也最容…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑