设计模式——装饰器设计模式(结构型)

article/2025/6/23 9:03:04

摘要

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

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. ✅ 适合使用装饰器模式的场景

场景

说明

需要扩展对象功能且不影响原类代码

不修改原类,动态增加功能,如日志、鉴权、限流。

需要在运行时动态组合多种行为

例如对同一个服务动态添加多种增强层:缓存、监控、安全等。

功能可以“嵌套组合”叠加

类似“加配料”的逻辑,如流式处理、文本格式处理、消息管道处理。

不能使用继承或继承组合会导致类爆炸

多个子类组合场景,如A+B+C,用装饰器组合比继承更灵活。

希望通过组合替代继承,增强可扩展性

符合“开闭原则”,增加功能时只加类、不改原有代码。

Spring 项目中常见增强逻辑(AOP 本质)

权限验证、日志记录、事务管理、指标统计等。

4.2. ❌ 不适合使用装饰器模式的场景

场景

原因

功能变化不是可组合的,而是互斥的

例如“单选”的策略或“开关”逻辑,不适合一层层叠加。

装饰层次太多导致调用链复杂、调试困难

层数一多,调用顺序和调试堆栈难以理解和排查。

功能变化不明显,使用装饰器反而增加复杂度

只是简单逻辑改造,不值得为了“设计模式”增加结构。

需要共享状态或强耦合多个功能模块

装饰器是包装独立功能,若需要强耦合交互,不合适。

对性能极度敏感、低延迟要求场景

层层封装增加方法调用和对象创建成本,可能不划算。

4.3. 🧠 总结对比表格

项目

✅ 适合使用

❌ 不适合使用

是否需要动态增强

✅ 是

❌ 否

是否支持组合功能

✅ 是

❌ 否,功能互斥

原类是否可修改

❌ 否(不能改)

✅ 是(能改就没必要)

是否需要多个扩展行为

✅ 是

❌ 否(只需单一变化)

是否可读性、调试性良好

✅ 层数少

❌ 层数多导致复杂

是否存在替代方案

❌ 无法用继承或策略代替

✅ 策略、简单组合更合适

5. 装饰器设计模式实战示例

在风控系统中,装饰器设计模式非常适合用来扩展规则校验、风控拦截、日志、监控、异常报警等横切功能,而且它不改变原有核心风控逻辑,具备很强的可组合性和可扩展性。

5.1. ✅ 业务场景:用户申请贷款,风控系统对其进行多层校验

5.2. 🎯 目标:

风控规则如下,可独立或组合使用:

  1. 黑名单校验
  2. 年龄校验
  3. 欺诈用户识别
  4. 日志记录

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 兼容性好

可以用 @Component + @Conditional 结合使用

5.11. 📦 Bonus:可结合策略模式 & 装饰器做更强动态配置

比如:

  • 风控策略由数据库或配置中心定义
  • 然后动态组合装饰器链
    是否也想看这个高级版本?我可以给你代码。

6. 装饰器设计模式思考

博文参考


http://www.hkcw.cn/article/mgaweiGDpW.shtml

相关文章

生活小记啊

最近生活上的事情还是蛮多的&#xff0c;想到哪写到哪。 工作 三月的某个周六&#xff0c;正在加班写技术方案&#xff0c;大晚上写完了听到调动通知&#xff0c;要去新的团队了。 还是蛮不舍的&#xff0c;看着产品从无到有&#xff0c;一路走过来&#xff0c;倾注了不少感…

【android bluetooth 案例分析 04】【Carplay 详解 2】【Carplay 连接之手机主动连车机】

1. 背景 在【android bluetooth 案例分析 04】【Carplay 详解 1】【CarPlay 在车机侧的蓝牙通信原理与角色划分详解】中我们从整理上介绍了车机中 carplay 相关基础概念。 本节 将详细分析 iphone手机主动 连接 车机carplay 这一过程。 先回顾一下 上一节&#xff0c; carpla…

【Kotlin】数字字符串数组集合

【Kotlin】简介&变量&类&接口 【Kotlin】数字&字符串&数组&集合 文章目录 Kotlin_数字&字符串&数组&集合数字字面常量显式转换数值类型转换背后发生了什么 运算字符串字符串模板字符串判等修饰符数组集合通过序列提高效率惰性求值序列的操…

FreeCAD源码分析: 串行化工具

本文分析FreeCAD中的串行化工具。 注1&#xff1a;限于研究水平&#xff0c;分析难免不当&#xff0c;欢迎批评指正。 注2&#xff1a;文章内容会不定期更新。 零、预修 0.1 QDataStream 0.2 Boost.Iostreams 0.3 Zipios 0.4 Xerces-C 一、核心组件 1.1 Base::Writer 1.2 Ba…

【R语言编程绘图-plotly】

安装与加载 在R中使用plotly库前需要安装并加载。安装可以通过CRAN进行&#xff0c;使用install.packages()函数。加载库使用library()函数。 install.packages("plotly") library(plotly)测试库文件安装情况 # 安装并加载必要的包 if (!requireNamespace("p…

设计模式——系统数据建模设计

摘要 本文主要介绍了UML在软件系统分析和设计中的应用&#xff0c;详细阐述了六大类关系&#xff08;泛化、实现、依赖、关联、聚合、组合&#xff09;及其在UML类图中的表示方法&#xff0c;并通过具体例子说明了这些关系在实际编程中的应用。同时&#xff0c;文章还概述了UM…

37. Sudoku Solver

题目描述 37. Sudoku Solver 回溯 class Solution {vector<vector<bool>> row_used;vector<vector<bool>> col_used;vector<vector<bool>> box_used;public:void solveSudoku(vector<vector<char>>& board) {row_used.r…

【Java开发日记】基于 Spring Cloud 的微服务架构分析

目录 1、Spring Cloud 2、Spring Cloud 的核心组件 1. Eureka&#xff08;注册中心&#xff09; 2. Zuul&#xff08;服务网关&#xff09; 3. Ribbon&#xff08;负载均衡&#xff09; 4. Hystrix&#xff08;熔断保护器&#xff09; 5. Feign&#xff08;REST转换器&a…

进程间通信IV System V 系列(linux)

目录 消息队列 原理 操作 补充概念 信号量 (原子性计数器) 原理 操作 (和共享内存相似) 总结 小知识 消息队列 原理 在内核中建立一个队列&#xff0c;进程可以相互进行通信&#xff0c;通过队列进行IPC&#xff0c;就是进程之间发送带类型的数据块。 操作 接口和共享…

【MySQL】索引(B+树详解)

MySQL(五)索引 一、索引的减I/O设计 1.读取量 2.搜索树 2.1方向 2.2有序 3.分多叉 3.1B树 弊端: 3.2B树 3.2.1非叶子-搜索字段 3.2.1.1海量分叉 3.2.1.1.1最大式 3.2.1.1.2最快式 3.2.1.2缓存内存 3.2.1.2.1字段总量小 3.2.1.2.2时间复杂度 3.2.1.3区间搜索向…

2025年全国青少年信息素养大赛复赛C++算法创意实践挑战赛真题模拟强化训练(试卷4:共计6题带解析)

2025年全国青少年信息素养大赛复赛C++算法创意实践挑战赛真题模拟强化训练(试卷4:共计6题带解析) 第1题:最佳情侣身高差(题目及解析) 题目描述 专家通过多组情侣研究数据发现,最佳的情侣身高差遵循着一个公式:(女方的身高) 1.09 =(男方的身高)。如果符合,你俩的身…

5.31 day33

知识点回顾&#xff1a; PyTorch和cuda的安装 查看显卡信息的命令行命令&#xff08;cmd中使用&#xff09; cuda的检查 简单神经网络的流程 数据预处理&#xff08;归一化、转换成张量&#xff09; 模型的定义 继承nn.Module类 定义每一个层 定义前向传播流程 定义损失函数和优…

【C++】模板

目录 1、函数模板 基本用法 函数模板的实现原理 函数模板的实例化 模板参数的匹配原则 2、类模板 类模板的定义格式 类模板的实例化 1、函数模板 基本用法 template < typename T >返回值类型 函数名(参数列表){} template 是模板的意思&#xff0c;typename是…

第六十二节:深度学习-加载 TensorFlow/PyTorch/Caffe 模型

在计算机视觉领域,OpenCV的DNN(深度神经网络)模块正逐渐成为轻量级模型部署的利器。本文将深入探讨如何利用OpenCV加载和运行三大主流框架(TensorFlow、PyTorch、Caffe)训练的模型,并提供完整的代码实现和优化技巧。 一、OpenCV DNN模块的核心优势 OpenCV的DNN模块自3.3…

Linux系统下安装配置 Nginx

Windows Nginx https://nginx.org/en/download.htmlLinux Nginx https://nginx.org/download/nginx-1.24.0.tar.gz解压 tar -zxvf tar -zxvf nginx-1.18.0.tar.gz #解压安装依赖&#xff08;如未安装&#xff09; yum groupinstall "Development Tools" -y yum…

qwen3解读

1. 模型架构 重点&#xff1a; 思维模式和非思维模式这两种不同的操作模式集成到一个模型中。这样可以让用户在这些模式间切换&#xff0c;而不是在不同模型间切换。多阶段的后培训方法&#xff1a;增强推理和非推理模式。将基础模型和人的偏好结合。 预训练阶段&#xff1a…

用127.0.0.1访问VMware虚拟机中间件

文章目录 前言一、虚拟机安装、插件安装二、改虚拟机配置1.更改虚拟机使用的是 NAT 模式2.编辑 VMware 的 NAT 设置并添加端口转发 总结至此大功告成了&#xff01;&#xff01;&#xff01; 前言 docker 安装中间件很方便,平时使用windows作开发,如果想快速安装中间件,那么首…

【大模型部署】mac m1本地部署 ChatGLM3-6B 超详细教程

本人环境&#xff1a;macOS 15.5 (Sonoma) - Apple M1 / 16 G 目标&#xff1a;在 mac m1 16G 上 完全离线 的本地模型目录上&#xff0c;跑通官方 ChatGLM3-6B 目录 背景 & 踩坑记录 准备工作 新建 Conda 环境并安装依赖 关键环境变量 运行 composite_demo 常见报错与…

评标专家随机抽选系统-建设方案——仙盟创梦IDE

政策要求 抽选记录保存 专辑库管理 系统统计 抽选打印 系统分级管理 项目管理 系统分级管理 集团公司管理 专家补抽 评标专家随机抽选 1、系统分级管理合规 符合《数据安全法》第二十一条&#xff08;分类分级制度&#xff09;&#xff0c;安全按照不同职级&#xff0c;配置权…

2.5/Q2,Charls最新文章解读

文章题目&#xff1a;Trajectories of depressive symptoms and risk of chronic liver disease: evidence from CHARLS DOI&#xff1a;10.1186/s12876-025-03943-7 中文标题&#xff1a;抑郁症状的轨迹和慢性肝病风险&#xff1a;来自 CHARLS 的证据 发表杂志&#xff1a;BM…