设计模式——装饰器设计模式(结构型)
摘要
文中主要介绍了装饰器设计模式,它是一种结构型设计模式,可在不改变原有类代码的情况下,动态为对象添加额外功能。文中详细阐述了装饰器模式的角色、结构、实现方式、适合场景以及实战示例等内容,还探讨了其与其他设计模式的结合使用,帮助读者全面理解装饰器设计模式。
1. 装饰器设计模式定义
装饰器模式是一种结构型设计模式,它允许在不改变原有类代码的前提下,动态地为对象添加额外的功能。
- 装饰器就像**“套娃”或“穿衣服”**,一层一层包裹原始对象,增强它的功能。
- 被装饰的对象不知道自己被增强了,增强逻辑是外部“包”实现的。
角色说明:
角色 | 含义 |
Component(抽象构件) | 原始功能的抽象定义,通常是一个接口或抽象类。 |
ConcreteComponent(具体构件) | 原始功能的具体实现类。 |
Decorator(装饰器抽象类) | 包装 Component,并定义扩展接口,通常持有一个 Component 引用。 |
ConcreteDecorator(具体装饰器) | 扩展 Component 功能的类,实现增强逻辑。 |
2. 装饰器设计模式结构
2.1. 装饰器类图
装饰模式包含如下角色:
- Component: 抽象构件
- ConcreteComponent: 具体构件
- Decorator: 抽象装饰类
- ConcreteDecorator: 具体装饰类
2.2. 装饰器时序图
3. 装饰器设计模式实现方式
装饰器设计模式有三种常见实现方式,关键思想是一致的:将原对象“包裹”起来,扩展功能而不改变原代码。
3.1. 常见实现方式
实现方式 | 描述 | 推荐度 |
接口 + 抽象类(经典做法) | 定义接口 + 装饰器抽象类 + 具体装饰类 | ⭐⭐⭐⭐ |
仅使用接口或抽象类(无抽象装饰器) | 简化结构,直接实现装饰逻辑 | ⭐⭐⭐ |
使用 Java 动态代理或 Spring AOP | 用代理机制动态增强对象行为 | ⭐⭐⭐⭐⭐(现代项目常用) |
3.2. 🧱 示例:给“原始服务”动态添加日志和鉴权功能
3.2.1. 🎯 目标接口(Component)
public interface OrderService {void placeOrder(String user);
}
3.2.2. ✅ 原始类(ConcreteComponent)
public class SimpleOrderService implements OrderService {@Overridepublic void placeOrder(String user) {System.out.println("下单成功:" + user);}
}
3.3. ✅ 实现方式一:接口 + 抽象装饰器类(经典方式)
3.3.1. 🎯 抽象装饰器类(Decorator)
public abstract class OrderServiceDecorator implements OrderService {protected OrderService delegate;public OrderServiceDecorator(OrderService delegate) {this.delegate = delegate;}
}
3.3.2. 🧩 具体装饰器 1:日志功能
public class LoggingOrderService extends OrderServiceDecorator {public LoggingOrderService(OrderService delegate) {super(delegate);}@Overridepublic void placeOrder(String user) {System.out.println("[日志] 用户:" + user + " 正在下单...");delegate.placeOrder(user);}
}
3.3.3. 🧩 具体装饰器 2:权限功能
public class AuthOrderService extends OrderServiceDecorator {public AuthOrderService(OrderService delegate) {super(delegate);}@Overridepublic void placeOrder(String user) {if (!"admin".equals(user)) {throw new SecurityException("无权限");}delegate.placeOrder(user);}
}
3.3.4. ✅ 使用方式:
OrderService service = new SimpleOrderService();
service = new LoggingOrderService(service);
service = new AuthOrderService(service);service.placeOrder("admin"); // ✅ 正常
// service.placeOrder("guest"); // ❌ 抛出无权限异常
3.4. ✅ 实现方式二:Spring AOP(自动代理装饰)
如果你用 Spring,可以用 AOP 注解来“无侵入式”地增强行为 —— 本质也是装饰器。
@Aspect
@Component
public class LogAspect {@Before("execution(* com.example.OrderService.placeOrder(..))")public void logBefore() {System.out.println("[AOP日志] 开始下单...");}
}
3.5. ✅ 实现方式三:Java 动态代理(JDK Proxy)
OrderService target = new SimpleOrderService();
OrderService proxy = (OrderService) Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class[]{OrderService.class},(proxyObj, method, args) -> {System.out.println("[代理] 下单前检查");return method.invoke(target, args);}
);
proxy.placeOrder("admin");
3.6. ✅ 装饰器实现方式总结
实现方式 | 特点 | 场景推荐 |
抽象类 + 接口 | 结构清晰、经典实现 | 适合手动控制、多个装饰层 |
仅接口实现 | 简洁灵活 | 适合小项目或简单增强 |
动态代理 / Spring AOP | 自动增强、无侵入 | Spring 项目首选 |
4. 装饰器设计模式适合场景
4.1. ✅ 适合使用装饰器模式的场景
场景 | 说明 |
需要扩展对象功能且不影响原类代码 | 不修改原类,动态增加功能,如日志、鉴权、限流。 |
需要在运行时动态组合多种行为 | 例如对同一个服务动态添加多种增强层:缓存、监控、安全等。 |
功能可以“嵌套组合”叠加 | 类似“加配料”的逻辑,如流式处理、文本格式处理、消息管道处理。 |
不能使用继承或继承组合会导致类爆炸 | 多个子类组合场景,如 |
希望通过组合替代继承,增强可扩展性 | 符合“开闭原则”,增加功能时只加类、不改原有代码。 |
Spring 项目中常见增强逻辑(AOP 本质) | 权限验证、日志记录、事务管理、指标统计等。 |
4.2. ❌ 不适合使用装饰器模式的场景
场景 | 原因 |
功能变化不是可组合的,而是互斥的 | 例如“单选”的策略或“开关”逻辑,不适合一层层叠加。 |
装饰层次太多导致调用链复杂、调试困难 | 层数一多,调用顺序和调试堆栈难以理解和排查。 |
功能变化不明显,使用装饰器反而增加复杂度 | 只是简单逻辑改造,不值得为了“设计模式”增加结构。 |
需要共享状态或强耦合多个功能模块 | 装饰器是包装独立功能,若需要强耦合交互,不合适。 |
对性能极度敏感、低延迟要求场景 | 层层封装增加方法调用和对象创建成本,可能不划算。 |
4.3. 🧠 总结对比表格
项目 | ✅ 适合使用 | ❌ 不适合使用 |
是否需要动态增强 | ✅ 是 | ❌ 否 |
是否支持组合功能 | ✅ 是 | ❌ 否,功能互斥 |
原类是否可修改 | ❌ 否(不能改) | ✅ 是(能改就没必要) |
是否需要多个扩展行为 | ✅ 是 | ❌ 否(只需单一变化) |
是否可读性、调试性良好 | ✅ 层数少 | ❌ 层数多导致复杂 |
是否存在替代方案 | ❌ 无法用继承或策略代替 | ✅ 策略、简单组合更合适 |
5. 装饰器设计模式实战示例
在风控系统中,装饰器设计模式非常适合用来扩展规则校验、风控拦截、日志、监控、异常报警等横切功能,而且它不改变原有核心风控逻辑,具备很强的可组合性和可扩展性。
5.1. ✅ 业务场景:用户申请贷款,风控系统对其进行多层校验
5.2. 🎯 目标:
风控规则如下,可独立或组合使用:
- 黑名单校验
- 年龄校验
- 欺诈用户识别
- 日志记录
5.3. 🧱 定义风控接口(Component)
public interface RiskCheck {boolean check(RiskContext context);
}
5.4. 🧱 定义风控上下文(数据输入)
@Data
public class RiskContext {private String userId;private int age;private boolean inBlacklist;private boolean isFraud;// 构造、getter/setter 省略
}
5.5. 🧱 基础校验类(ConcreteComponent)
public class BasicRiskCheck implements RiskCheck {@Overridepublic boolean check(RiskContext context) {return true; // 默认无拦截}
}
5.6. 🧱 装饰器抽象类(Decorator)
public abstract class RiskCheckDecorator implements RiskCheck {protected RiskCheck delegate;public RiskCheckDecorator(RiskCheck delegate) {this.delegate = delegate;}
}
5.7. ✅ 具体装饰器实现(ConcreteDecorator)
5.7.1. 黑名单校验:
public class BlacklistCheck extends RiskCheckDecorator {public BlacklistCheck(RiskCheck delegate) {super(delegate);}@Overridepublic boolean check(RiskContext context) {if (context.isInBlacklist()) {System.out.println("拦截:用户在黑名单中");return false;}return delegate.check(context);}
}
5.7.2. 年龄校验:
public class AgeCheck extends RiskCheckDecorator {public AgeCheck(RiskCheck delegate) {super(delegate);}@Overridepublic boolean check(RiskContext context) {if (context.getAge() < 18) {System.out.println("拦截:未成年人禁止贷款");return false;}return delegate.check(context);}
}
5.7.3. 欺诈识别:
public class FraudCheck extends RiskCheckDecorator {public FraudCheck(RiskCheck delegate) {super(delegate);}@Overridepublic boolean check(RiskContext context) {if (context.isFraud()) {System.out.println("拦截:疑似欺诈用户");return false;}return delegate.check(context);}
}
5.7.4. 日志记录:
public class LoggingCheck extends RiskCheckDecorator {public LoggingCheck(RiskCheck delegate) {super(delegate);}@Overridepublic boolean check(RiskContext context) {System.out.println("执行风控检查...");boolean result = delegate.check(context);System.out.println("风控结果:" + result);return result;}
}
5.8. ✅ 组装装饰链(动态组合)
RiskCheck riskCheck = new BasicRiskCheck();
riskCheck = new LoggingCheck(riskCheck);
riskCheck = new BlacklistCheck(riskCheck);
riskCheck = new AgeCheck(riskCheck);
riskCheck = new FraudCheck(riskCheck);
5.9. 🧪 调用示例
RiskContext context = new RiskContext();
context.setUserId("u123");
context.setAge(20);
context.setInBlacklist(false);
context.setFraud(false);boolean result = riskCheck.check(context);
System.out.println("最终是否通过风控:" + result);
5.10. 🧠 总结优势
优点 | 描述 |
动态组合风控规则 | 根据不同产品或渠道,组合不同装饰器 |
遵守开闭原则 | 添加新风控规则无需改动原有类,只需新增装饰器类 |
可重用、可插拔 | 每个装饰器是一个独立功能模块,支持复用 |
与 Spring 兼容性好 | 可以用 |
5.11. 📦 Bonus:可结合策略模式 & 装饰器做更强动态配置
比如:
- 风控策略由数据库或配置中心定义
- 然后动态组合装饰器链
是否也想看这个高级版本?我可以给你代码。
6. 装饰器设计模式思考
博文参考
相关文章:

设计模式——装饰器设计模式(结构型)
摘要 文中主要介绍了装饰器设计模式,它是一种结构型设计模式,可在不改变原有类代码的情况下,动态为对象添加额外功能。文中详细阐述了装饰器模式的角色、结构、实现方式、适合场景以及实战示例等内容,还探讨了其与其他设计模式的…...

途景VR智拍APP:开启沉浸式VR拍摄体验
在数字化时代,VR技术以其沉浸式的体验逐渐走进了人们的日常生活。途景VR智拍APP作为一款集看图和拍照于一体的VR软件,为用户带来了全新的视觉体验和便捷的拍摄方式,无论是专业摄影师还是普通用户,都能轻松上手,拍出令人…...

Linux环境搭建MCU开发环境
操作系统版本: ubuntu 22.04 文本编辑器: vscode 开发板: stm32f103c8t6 调试器: st-link 前言 步骤一: 安装交叉编译工具链 步骤二: 创建工程目录结构 步骤三: 调试…...
Android高级开发第一篇 - JNI(初级入门篇)
文章目录 Android高级开发JNI开发第一篇(初级入门篇)🧠 一、什么是 JNI?✅ 为什么要用 JNI? ⚙️ 二、开发环境准备开发工具 🚀 三、创建一个支持 JNI 的 Android 项目第一步:创建新项目项目结构…...
Kubernetes RBAC权限控制:从入门到实战
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言:为什么需要RBAC? 在Kubernetes集群中,权限失控是导致安全漏洞的核心原因之一。试想以下场景: 开发…...
python实战项目71:基于Python的US News世界大学排名数据爬取
python实战项目71:基于Python的US News世界大学排名数据爬取 一、项目背景1.1 研究意义1.2 技术背景1.3 应用场景二、爬虫系统设计与实现2.1 分析页面、寻找数据真实接口2.2 发送请求,获取响应内容2.3 提取数据2.4 保存数据三、完整代码四、总结与展望一、项目背景 1.1 研究…...

【基础算法】高精度(加、减、乘、除)
文章目录 什么是高精度1. 高精度加法解题思路代码实现 2. 高精度减法解题思路代码实现 3. 高精度乘法解题思路代码实现 4. 高精度除法 (高精度 / 低精度)解题思路代码实现 什么是高精度 我们平时使用加减乘除的时候都是直接使用 - * / 这些符号,前提是进行运算的数…...
跨平台开发框架electron
桌面端开发框架有很多,比如C#的WPF和Winform,Dart的Flutter,JS的Electron,Rust的Tauri。 目前应用比较广的是Electron,比如我们常见的开发工具VsCode,就是基于Electron开发的。 所以这篇文章我们就来聊聊Electron。 简…...

Windows最快速打开各项系统设置大全
目录 一、应用背景 二、设置项打开方法 2.1 方法一界面查找(最慢) 2.2 方法二cmd命令(慢) 2.3 方法三快捷键(快) 2.4 方法四搜索栏(快) 2.5 方法五任务栏(最快&am…...

嵌入式编译工具链熟悉与游戏移植
在自己的虚拟机Ubuntu系统下,逐步编译 mininim源码(波斯王子重制开源版) 指令流程 sudo apt-get remove liballegro5-dev liballegro-image5-dev \liballegro-audio5-dev liballegro-acodec5-dev liballegro-dialog5-dev sudo apt-get install automak…...

DeepSeek-R1-0528,官方的端午节特别献礼
DeepSeek:端午安康!刻在国人骨子里的浪漫 2025 年 05 月 28 日 | DeepSeek 端午特别献礼 当粽叶飘香时,DeepSeek 悄然带来一份节日惊喜 版本号 DeepSeek-R1-0528 正式上线 官方赋予它的灵魂是: 思考更深 推理更强 用户通过官网…...
LNMP环境中php7.2升级到php7.4
以下是 CentOS 7 上从 PHP 7.2 升级到 PHP 7.4 的详细步骤,结合知识库中的方法和注意事项: 1.备份现有环境 #备份 PHP 配置文件 cp /etc/php.ini /etc/php.ini.bak cp -r /etc/php.d /etc/php.d.bak#备份网站文件和数据库 tar -czvf website_backup.tar…...

001 flutter学习的注意事项及前期准备
在学习flutter之前,还需要进行一些初始的配置,然后才可以学习flutter 1.安装flutter 国内官网:https://flutter.cn 国际官网:https://flutter.dev 安装完成后,按照官网上面的操作步骤进行配置…...
FactoryBean 接口
Spring 框架中 FactoryBean 接口的特性,这是 Spring 提供的一种特殊机制,用于创建和管理复杂 Bean。让我通过示例和解释帮您理解这个概念。 一、FactoryBean 是什么? FactoryBean 是 Spring 框架提供的一个工厂接口,用于创建复杂…...

CS144 - Lecture 1 记录
CS144 - Lecture 1 由于没讲义,全看课了,系统性的总结有点难,记一些有趣的东西吧。 数据链路和网络层的传输 我们可以看见,对于发送方,我们的数据链路层为我们的网络层提供服务,在经过路由的时候…...
【Redis】大key问题详解
目录 1、什么是大key2、大key的危害【1】阻塞风险【2】网络阻塞【3】内存不均【4】持久化问题 3、如何发现大key【1】使用内置命令【2】使用memory命令(Redis 4.0)【3】使用scan命令【4】监控工具 4、解决方案【1】拆分大key【2】使用合适的数据结构【3】…...

【数据结构】——二叉树--链式结构
一、实现链式结构二叉树 二叉树的链式结构,那么从名字上我们就知道我们这个二叉树的底层是使用链表来实现的,前面我们的二叉树是通过数组来实现的,那么在其是完全二叉树的情况下,此时我们使用数组来实现就会使得其空间浪费较少&a…...
TKernel模块--杂项
TKernel模块–杂项 1.DEFINE_HARRAY1 #define DEFINE_HARRAY1(HClassName, _Array1Type_) \ class HClassName : public _Array1Type_, public Standard_Transient { \public: …...

充电便捷,新能源汽车移动充电服务如何预约充电
随着新能源汽车的普及,充电便捷性成为影响用户体验的关键因素之一。传统的固定充电桩受限于地理位置和数量,难以完全满足用户需求,而移动充电服务的出现,为车主提供了更加灵活的补能方式。通过手机APP、小程序或在线平台ÿ…...
laya3的2d相机与2d区域
2d相机和2d区域都继承自Sprite。 2d相机必须作为2d区域的子节点,且2d相机必须勾选isMain才能正常使用。 2d区域下如果没有主相机,则他和Sprite无异,他的主要操作皆是针对主相机。 2d相机可以调整自己的移动范围,是否紧密跟随&a…...
2024 CKA模拟系统制作 | Step-By-Step | 19、题目搭建-升级集群
目录 免费获取题库配套 CKA_v1.31_模拟系统 一、题目 二、考点分析 1. Kubernetes 升级策略 2. 节点维护操作 3. 组件升级技术 4. 权限与访问控制 三、考点详细讲解 1. Kubernetes 升级流程 2. 组件版本兼容性 3. drain 操作深度解析 四、实验环境搭建步骤 五、总…...
47道ES67高频题整理(附答案背诵版)
1.ES5、ES6(ES2015)有什么区别? ES5(ECMAScript 5)和ES6(也称为ECMAScript 2015)是JavaScript语言的两个版本,它们之间有一些重要的区别和改进: let 和 const 关键字: …...
Lauterbach TRACE32专栏
官方培训视频 trace32使用技巧博文 系统崩溃分析 - vmcore 加载到 Trace32 Trace 32 离线 dump 分析环境搭建方法 内核trace分析工具入门 如何用Trace32分析内核死机 trace32调试攻略 TRACE32调试:基础调试技巧之SystemMode、SNOOPer https://cloud.tencent…...

基于 Chrome 浏览器扩展的Chroma简易图形化界面
简介 ChromaDB Manager 是基于 Chrome 浏览器扩展的一款 ChromaDB(一个流行的向量数据库)的数据查询工具。提供了一个用户友好的界面,可以直接从浏览器连接到本地 ChromaDB 实例、查看集合信息和分片数据。本工具特别适合开发人员快速查看和…...
python打卡day41
简单CNN 数据增强卷积神经网络定义的写法batch归一化:调整一个批次的分布,常用与图像数据特征图:只有卷积操作输出的才叫特征图调度器:直接修改基础学习率 一、数据增强 在图像数据预处理环节,为提升数据多样性&#x…...

IM系统的负载均衡
1.IM场景的负载均衡 2.方案总览 SDK层想要连接一个TCP网关或者WebSocket网关的方案 SDK单地址:在SDK中写死某个网关的IP或者域名,缺点是更换地址需要重新打包SDK SDK多地址:防止某一个地址嗝屁了写上多个地址用足保持高可用 暴露接口给客户端:SDK层访问接口动态获得地址 注…...
前端八股 tcp 和 udp
都是传输层协议 udp 数据报协议 不可靠面向数据包对于应用层传递的报文加上UDP首部就传给网络层 tcp 传输控制协议 可靠 会将报文分段进行传输 区别: 1.tcp 可靠 udp 不可靠 2.tcp 面向连接 三握四挥 udp 无连接 3.tcp面向字节流 udp面向报文 4.效率低 效率高…...

使用 Zabbix 监控 MySQL 存储空间和性能指标的完整实践指南
目录 引言 一、最终目标支持功能 二、监控方案设计 2.1 技术选型 2.2 设计思路 三、实现步骤 3.1 准备工作 3.11 创建 MySQL 监控账号 3.12 配置 .my.cnf 文件 3.2 编写统一脚本 3.3 配置 Zabbix Agent UserParameter 3.4 Zabbix 前端配置建议 四、总结 引言 MySQL …...

【技能拾遗】——家庭宽带单线复用布线与配置(移动2025版)
📖 前言:在家庭网络拓扑中,客厅到弱电箱只预埋了一根网线,由于已将广电的有线电视取消并改用IPTV。现在需要解决在客厅布置路由器和观看IPTV问题,这里就用到单线复用技术。 目录 🕒 1. 拓扑规划ὕ…...

异步日志监控:FastAPI与MongoDB的高效整合之道
title: 异步日志监控:FastAPI与MongoDB的高效整合之道 date: 2025/05/27 17:49:39 updated: 2025/05/27 17:49:39 author: cmdragon excerpt: FastAPI与MongoDB整合实现日志监控系统的实战指南。首先配置MongoDB异步连接,定义日志数据模型。核心功能包括日志写入接口、聚合…...