《程序猿之设计模式实战 · 装饰者模式》
📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍
文章目录
- 写在前面的话
- 基础介绍
- 代码实现
- Spring 使用装饰者模式
- 动态代理VS装饰者模式
- 装饰者模式的简化
- 总结陈词

写在前面的话
上一篇文章《程序猿之设计模式实战 · 策略模式》介绍的了策略模式的实际运用,这篇紧随其后,补充上装饰者模式。
装饰者模式也是相当实用的,适合很多场景,且听慢慢道来。
基础介绍
基础概念:
装饰者模式是一种结构型设计模式,它允许在不改变对象自身的情况下,动态地给对象添加新的功能。通过将功能封装在装饰类中,装饰者模式提供了一种灵活的方式来扩展对象的行为。
装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。
类图:

组成部分:
装饰者模式通常由以下几个部分组成:
1、组件接口(Component):定义一个接口,声明具体组件和装饰者都需要实现的方法。
2、具体组件(ConcreteComponent):定义一个将要接收附加责任的类,实现组件接口,表示被装饰的对象。
3、装饰者(Decorator):也是实现组件接口的类,持有一个组件对象的引用,并在其方法中调用该组件的方法。装饰者可以在调用前后添加额外的功能。
4、具体装饰者(ConcreteDecorator):继承自装饰者类,具体实现添加的功能。
常用场景:
装饰者模式适用于以下场景:
1、需要动态添加功能:当你希望在运行时为对象添加功能,而不影响其他对象时。
2、避免子类爆炸:当功能组合的数量较多时,使用装饰者模式可以避免创建大量的子类。
3、增强类的功能:当你希望在不修改现有类的情况下,增强类的功能。
代码实现
示例:以文本输出,添加粗体、斜体的职责为例。
Step1、定义组件接口
public interface Text {String getContent();
}
Step2、定义具体组件
public class BaseText implements Text {private final String content;public BaseText(String content) {this.content = content;}@Overridepublic String getContent() {return content;}
}
Step3、定义装饰者
public abstract class TextDecorator implements Text {protected Text text;public TextDecorator(Text text) {this.text = text;}
}
Step4、定义具体装饰者
public class BoldTextDecorator extends TextDecorator {public BoldTextDecorator(Text text) {super(text);}@Overridepublic String getContent() {return "<b>" + text.getContent() + "</b>";}
}public class ItalicTextDecorator extends TextDecorator {public ItalicTextDecorator(Text text1) {super(text1);}@Overridepublic String getContent() {return "<i>" + text.getContent() + "</i>";}
}
Step5、客户端测试
public class DecoratorClient {public static void main(String[] args) {Text plainText = new BaseText("Hello, 战神!");// 添加粗体装饰Text boldTextDecorator = new BoldTextDecorator(plainText);System.out.println(boldTextDecorator.getContent());// 输出内容:// <b>Hello, 战神!</b>// 添加斜体装饰Text italicTextDecorator = new ItalicTextDecorator(boldTextDecorator);System.out.println(italicTextDecorator.getContent());// 输出内容:// <i><b>Hello, 战神!</b></i>}
}
点评一下:
从代码看,还是挺清晰的,各司其职。
可以理解为不影响原有功能,动态进行功能的增强就是装饰者模式。
特别适合解决例如购物、点餐、SKU等组合的情况,即容易出现“类爆炸”的场合。
Spring 使用装饰者模式
在 Spring 框架中,装饰者模式被广泛应用于以下几个方面:
1、AOP(面向切面编程):
Spring AOP 使用代理模式来实现切面功能,实际上可以看作是对目标对象的装饰。通过在目标对象周围添加切面逻辑(如事务管理、日志记录等),而不改变目标对象的代码。
2、Spring MVC 的 HandlerInterceptor:
在 Spring MVC 中,HandlerInterceptor 可以被视为一种装饰者模式的实现。它允许在请求处理的不同阶段(如请求前、请求后)添加额外的处理逻辑,而不需要修改控制器本身的代码。
3、BeanPostProcessor:
Spring 的 BeanPostProcessor 接口允许在 Spring 容器创建和初始化 bean 之后,添加额外的功能或修改 bean 的属性。这种机制也可以看作是对 bean 的装饰。
总结:装饰者模式是一种灵活的设计模式,允许在运行时动态地为对象添加功能。在 Spring 框架中,装饰者模式的思想被广泛应用于 AOP、拦截器和 bean 处理等多个方面,使得功能的扩展变得更加灵活和可维护。
动态代理VS装饰者模式
相似之处:
细心的伙伴可能观察到,日常接触的动态代理,是否装饰者模式虽然有相似之处。
1、从增强功能上看,两者都可以在不修改原有类的情况下,为对象添加额外的功能或行为。
2、从代码结构上看:都涉及到对原有对象的封装,通常通过组合或代理的方式来实现。
但其实两者还是存在区别:
1、实现方式:
装饰者模式:通常通过创建一个装饰者类来包装原有对象,装饰者类实现与原有对象相同的接口,并在其方法中调用原有对象的方法,同时添加新的行为。
动态代理:使用反射机制在运行时创建一个代理对象,该代理对象可以在方法调用时添加额外的逻辑。动态代理不需要创建具体的装饰类,而是通过代理类来增强功能。
2、使用场景:
装饰者模式:适用于需要在多个地方以不同方式增强对象的场景,通常是为了增加对象的功能。
动态代理:适用于需要在运行时决定增强逻辑的场景,比如 AOP(面向切面编程)中的横切关注点。
3、灵活性:
装饰者模式:需要在编译时确定装饰的组合,通常是静态的。
动态代理:可以在运行时动态决定增强的逻辑,具有更高的灵活性。
总结一下:
动态代理可以被视为一种特殊的装饰者模式实现,但它更侧重于运行时的动态性和灵活性,而装饰者模式则更关注于通过组合来增强对象的功能。因此,虽然它们有相似的目的,但在实现和使用上有明显的区别。
装饰者模式的简化
实际开发中,往往场景不会都是这么复杂(没这么多元素),此时可以考虑简化。
Tips:实战要灵活,学会变通,而不要一味追求标准、套用标准。
如果只有一个待装饰的类ConcreteComponent,那么可以考虑去掉抽象的 Component 类(接口),把Decorator作为一个ConcreteComponent子类。

如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而可以把Decorator和ConcreteDecorator的责任合并成一个类。

是不是很简洁,不过怎么越看越像JDK动态代理?
万变不如其宗,根源应该靠面向接口编程以及持有被代理/装饰的对象吧。
总结陈词
还是那句话,不用过多的纠结在用的是哪个设计模式,实现的是什么标准。
遇到实际问题能使用合适的方式解决,同时代码经得起推敲和扩展,才是最主要的。
回到装饰者模式,常用于解决“类爆炸”的问题,动态附加功能,同时基本满足面向对象开发原则的全部原则。
不过,装饰模式也有相应的缺点,由于使用装饰模式,可以比使用继承关系需要较少数目的类。使用较少的类,当然使设计比较易于进行。但是,在另一方面,使用装饰模式会产生比使用继承关系更多的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。
当然,装饰者还是很强的,遇到适合的场景果断用上,缺点可以忽略不计。
还是那句话,你可以不用,但不能不会。
💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。

相关文章:
《程序猿之设计模式实战 · 装饰者模式》
📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗 🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数…...
[K8S]Forbidden: pod updates may not change fields other than
背景 在执行kubectl apply -f pod-nginx.yaml的时候报错 The Pod "nginx-test" is invalid: spec: Forbidden: pod updates may not change fields other than `spec.containers[*].image`, `spec.initContainers[*].image`, `spec.activeDeadlineSeconds`, `spec.to…...
C/C++漏洞检测数据集汇总
漏洞检测这个方向最近几年尤为热门,尤其是与深度学习技术相结合的研究,同时一些公开可用的数据集的出现也进一步推动了这些技术的发展。本篇文章总结归纳了目前在 C/C 源代码漏洞检测方向的一些公开数据集以及相关文献。 1 Devign (FFmpegQemu) 简介&am…...
springboot后端开发-常见注解及其用途
文章目录 1. 组件注解2. 依赖注入注解3. 配置类注解4. 测试注解5. 控制器注解6. 安全和认证注解7. 切面相关注解8. API文档相关注解(需引入swagger)9. 其他注解 在Spring Boot框架中,有许多常用的注解用来简化开发过程中的依赖注入、组件扫描、配置、安全控制等方面…...
TypeScript 扩展
扩展 ?:可选参数 可选链事实上并不是TypeScript独有的特性,它是ES11(ES2020)中增加的特性 可选链使用可选链操作符 ? 作用是当对象的属性不存在时,会短路,直接返回undefined,如果存在,那么…...
按键学院往期视频
按键学院第五期 网游实战系列课程 按键学院第四期 网游实战系列课程01-回合制网游的特点:测试游戏后台按键图色 网游实战系列课程02-神武新手任务的识别与处理:字库识别任务 网游实战系列课程03-神武自动组队与攻击 网游实战系列课程04-神武自动逛地图与攻击 网游实战系列课程0…...
通信工程学习:什么是MRF多媒体资源功能、MRFC多媒体资源功能控制、MRFP多媒体资源功能处理
一、MRF多媒体资源功能 MRF(Multimedia Resource Function,多媒体资源功能)是3G/IMS网络中定义的提供多媒体资源功能的网络实体,它为3G/IMS网络的业务和承载提供媒体能力支持。MRF通过提供丰富的媒体处理功能,如播放声…...
【Windows】获取进程缓解策略设置情况
目录 一、前言 二、主要概念 三、实现步骤 四、总结 原文出处链接:[https://blog.csdn.net/qq_59075481/article/details/142234952] 一、前言 在现代操作系统中,进程缓解策略(Process Mitigation Policy)提供了一种防御机制…...
语音识别相关概念
声音如何保存成数字信号? 声音是听觉对声波产生的感知,而声波是一种在时间和振幅上连续的模拟量,本质是介质的振动,,比如空气的振动。那么只需要把这个振动信号记录下来,并用一串数字来表达振动信号振动的…...
Iceberg与SparkSQL查询操作整合
前言 spark操作iceberg之前先要配置spark catalogs,详情参考Iceberg与Spark整合环境配置。 Iceberg使用Apache Spark的DataSourceV2 API来实现数据源和catalog。 使用SQL查询 查询的时候表要按照:catalog.数据库.表名的格式 SELECT * FROM prod.db.table; -- catalog: p…...
Linux 上安装 PostgreSQL
Linux 上安装 PostgreSQL PostgreSQL 是一款功能强大的开源关系数据库管理系统,因其稳定性、可扩展性和先进的功能而广受欢迎。在 Linux 系统上安装 PostgreSQL 是一个相对直接的过程,但具体步骤可能会因您使用的 Linux 发行版而异。本文将介绍在几种流行的 Linux 发行版上安…...
WRF-LES与PALM微尺度气象大涡模拟、PALM静态数据预备、PALM驱动数据预报、PALM模拟
查看原文>>>WRF-LES与PALM微尺度气象大涡模拟及ChatGPT在大气科学领域应用 针对微尺度气象的复杂性,大涡模拟(LES)提供了一种无可比拟的解决方案。微尺度气象学涉及对小范围内的大气过程进行精确模拟,这些过程往往与天气…...
需求分析概述
为什么要进行需求分析呢? 笑话:富翁娶妻 某富翁想要娶老婆,有三个人选,富翁给了三个女孩各一千元,请 她们把房间装满。第一个女孩买了很多棉花,装满房间的1/2。第 二个女孩买了很多气球,装满…...
Java | Leetcode Java题解之第391题完美矩形
题目: 题解: class Solution {public boolean isRectangleCover(int[][] rectangles) {long area 0;int minX rectangles[0][0], minY rectangles[0][1], maxX rectangles[0][2], maxY rectangles[0][3];Map<Point, Integer> cnt new HashM…...
java项目之基于web的人力资源管理系统的设计与实现(源码+文档)
风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的基于web的人力资源管理系统的设计与实现。项目源码以及部署相关请联系风歌,文末附上联系信息 。 项目简介: …...
Linux 防火墙:iptables (二)
文章目录 SNAT 原理与应用SNAT 应用环境SNAT 原理SNAT 转换前提条件SNAT 格式SNAT 转换规则配置 DNAT 原理与应用DNAT 应用环境DNAT 原理DNAT 转换前提条件DNAT 格式DNAT 转换规则配置 iptables 规则的备份和还原导出(备份)所有表的规则导入(…...
小目标检测顶会新思路!最新成果刷爆遥感SOTA,参数小了18倍
遥感领域的小目标检测一直是个具有挑战性和趣味性的研究方向,同时也是顶会顶刊的常客。但不得不说,今年关于遥感小目标检测的研究热情尤其高涨,已经出现了很多非常优秀的成果。 比如SuperYOLO方法,通过融合多模态数据并执行高分辨…...
【Ubuntu】虚拟机安装USB摄像头ROS驱动 usb_cam(最新方法)
写在前面: 🌟 欢迎光临 清流君 的博客小天地,这里是我分享技术与心得的温馨角落。📝 个人主页:清流君_CSDN博客,期待与您一同探索 移动机器人 领域的无限可能。 🔍 本文系 清流君 原创之作&…...
免费的成绩查询微信小程序,让家长轻松掌握学生表现
传统的教学方式在不断地被革新。在成绩查询这一环节,老师们曾经面临着繁琐的手工操作和信息安全的风险。可现如今有一个让成绩查询变得轻松、高效且安全的新工具——易查分。 过去需要花费大量时间来整理成绩,然后通过短信或者打电话的方式告知给家长。以…...
[含视频和源码]CRUD的最佳实践,联动前后端,包含微信小程序,API,HTML等(三)
关说不练假把式,在上一,二篇中介绍了我心目中的CRUD的样子 基于之前的理念,我开发了一个命名为PasteTemplate的项目,这个项目呢后续会转化成项目模板,转化成项目模板后,后续需要开发新的项目就可以基于这…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
省略号和可变参数模板
本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...
c# 局部函数 定义、功能与示例
C# 局部函数:定义、功能与示例 1. 定义与功能 局部函数(Local Function)是嵌套在另一个方法内部的私有方法,仅在包含它的方法内可见。 • 作用:封装仅用于当前方法的逻辑,避免污染类作用域,提升…...
ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]
报错信息:libc.so.6: cannot open shared object file: No such file or directory: #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...
解析两阶段提交与三阶段提交的核心差异及MySQL实现方案
引言 在分布式系统的事务处理中,如何保障跨节点数据操作的一致性始终是核心挑战。经典的两阶段提交协议(2PC)通过准备阶段与提交阶段的协调机制,以同步决策模式确保事务原子性。其改进版本三阶段提交协议(3PC…...
