摘要
文中主要介绍了装饰器设计模式,它是一种结构型设计模式,可在不改变原有类代码的情况下,动态为对象添加额外功能。文中详细阐述了装饰器模式的角色、结构、实现方式、适合场景以及实战示例等内容,还探讨了其与其他设计模式的结合使用,帮助读者全面理解装饰器设计模式。
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 {
@Override
public 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);
}
@Override
public 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);
}
@Override
public 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 {
@Override
public 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);
}
@Override
public 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);
}
@Override
public 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);
}
@Override
public 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);
}
@Override
public 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:可结合策略模式 & 装饰器做更强动态配置
比如:
- 风控策略由数据库或配置中心定义
- 然后动态组合装饰器链
是否也想看这个高级版本?我可以给你代码。