设计模式:装饰器模式

欢迎你来读这篇博客,这篇博客主要是关于装饰器模式。
其中包括装饰器模式的核心思想、适用场景、优缺点、与代理模式和适配器模式的区别,以及 Java 后端开发中消息发送器增强的完整案例。

序言

在软件开发中,我们经常会遇到这样的需求:

已经有一个类可以完成核心功能,但现在想在不修改它的前提下,额外增加一些能力。

比如有一个短信发送器:

1
smsSender.send("13800000000", "验证码 9527");

现在希望在发送短信前后增加一些功能:

  • 打印日志;
  • 统计耗时;
  • 失败重试;
  • 限流保护;
  • 监控埋点;
  • 异常告警;
  • TraceId 追踪。

最直接的方式是修改原来的 SmsSender。

但是这样会带来几个问题:

  1. 原类越来越臃肿;
  2. 不同增强逻辑耦合在一起;
  3. 违反开闭原则;
  4. 不同组合方式难以复用;
  5. 想给邮件发送器、企业微信发送器也加同样能力时,会重复写代码。

这时就可以使用装饰器模式。

装饰器模式的核心思想是:

在不改变原对象结构的情况下,动态地给对象增加额外功能。

它就像给一个对象一层一层“穿装备”。

原对象负责核心能力。

装饰器负责增强能力。

对象本身不用改,但能力可以一层层叠上去。

这就像 Java 后端里的“加日志、加重试、加限流、加监控”,别一股脑塞进业务类,业务类不是圣诞树,别什么都往上挂。

正文

chapter 1:什么是装饰器模式

装饰器模式,英文是 Decorator Pattern,属于结构型设计模式。

它的定义是:

动态地给一个对象添加一些额外职责。就扩展功能而言,装饰器模式比生成子类更加灵活。

简单说:

装饰器模式通过包装对象来增强对象功能,而不是通过修改原类或大量继承子类来扩展功能。

装饰器模式通常包含四个角色:

  1. Component 抽象组件:定义被装饰对象和装饰器的共同接口。
  2. ConcreteComponent 具体组件:真正完成核心功能的对象。
  3. Decorator 抽象装饰器:持有一个 Component,并实现 Component 接口。
  4. ConcreteDecorator 具体装饰器:在调用原对象前后增加额外功能。

结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Client
│
▼
Component
▲
│
├── ConcreteComponent
│
└── Decorator
│
└── Component
▲
│
ConcreteDecorator

最关键的一点是:

装饰器和被装饰对象实现同一个接口。

所以装饰后的对象仍然可以当作原接口使用。

chapter 2:为什么需要装饰器模式

假设我们有一个消息发送接口。

1
2
3
4
public interface MessageSender {

void send(String receiver, String content);
}

短信发送器实现:

1
2
3
4
5
6
7
public class SmsMessageSender implements MessageSender {

@Override
public void send(String receiver, String content) {
System.out.println("发送短信给 " + receiver + ",内容:" + content);
}
}

现在要增加日志功能。

一种方式是直接改 SmsMessageSender:

1
2
3
4
5
6
7
8
9
10
11
public class SmsMessageSender implements MessageSender {

@Override
public void send(String receiver, String content) {
System.out.println("开始发送短信");

System.out.println("发送短信给 " + receiver + ",内容:" + content);

System.out.println("短信发送完成");
}
}

然后又要增加重试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SmsMessageSender implements MessageSender {

@Override
public void send(String receiver, String content) {
System.out.println("开始发送短信");

try {
System.out.println("发送短信给 " + receiver + ",内容:" + content);
} catch (Exception e) {
System.out.println("发送失败,开始重试");
}

System.out.println("短信发送完成");
}
}

再加限流、监控、TraceId、异常告警,类就会越来越乱。

另一个方案是继承。

1
2
3
4
5
6
7
8
SmsMessageSender
├── LogSmsMessageSender
├── RetrySmsMessageSender
├── RateLimitSmsMessageSender
├── LogRetrySmsMessageSender
├── LogRateLimitSmsMessageSender
├── RetryRateLimitSmsMessageSender
└── LogRetryRateLimitSmsMessageSender

这会导致类爆炸。

如果增强能力有 n 个,组合数量可能接近:

1
2^n

装饰器模式的做法是:

每一种增强能力单独做成一个装饰器,需要什么能力,就一层一层包起来。

例如:

1
2
3
4
5
MessageSender sender = new LoggingMessageSenderDecorator(
new RetryMessageSenderDecorator(
new SmsMessageSender()
)
);

这样核心类不用改,增强能力也可以自由组合。

chapter 3:装饰器模式的核心思想

装饰器模式的核心可以概括为三句话:

  1. 被装饰对象和装饰器实现同一个接口;
  2. 装饰器内部持有一个同接口对象;
  3. 装饰器在调用原对象前后添加增强逻辑。

抽象装饰器通常长这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
public abstract class MessageSenderDecorator implements MessageSender {

protected final MessageSender delegate;

protected MessageSenderDecorator(MessageSender delegate) {
this.delegate = delegate;
}

@Override
public void send(String receiver, String content) {
delegate.send(receiver, content);
}
}

具体装饰器长这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class LoggingMessageSenderDecorator extends MessageSenderDecorator {

public LoggingMessageSenderDecorator(MessageSender delegate) {
super(delegate);
}

@Override
public void send(String receiver, String content) {
System.out.println("发送前记录日志");

delegate.send(receiver, content);

System.out.println("发送后记录日志");
}
}

装饰器不是替换原对象,而是增强原对象。

chapter 4:不用装饰器模式会怎样

如果不用装饰器模式,常见写法有两种。

1. 直接修改原类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class SmsMessageSender implements MessageSender {

@Override
public void send(String receiver, String content) {
checkRateLimit(receiver);

long start = System.currentTimeMillis();

try {
doSend(receiver, content);
recordMetrics(System.currentTimeMillis() - start);
} catch (Exception e) {
retry(receiver, content);
sendAlarm(e);
}

writeLog(receiver, content);
}
}

这种写法的问题是:

  • 发送逻辑和增强逻辑混在一起;
  • 类职责变重;
  • 修改风险增大;
  • 不同增强逻辑难以复用;
  • 不同渠道也要重复写类似增强代码。

2. 使用大量继承

1
2
3
4
5
6
7
8
9
10
SmsSender
EmailSender

LogSmsSender
RetrySmsSender
LogRetrySmsSender

LogEmailSender
RetryEmailSender
LogRetryEmailSender

这种写法的问题是:

  • 类数量爆炸;
  • 组合不灵活;
  • 新增一个增强能力,要扩展很多类;
  • 新增一个发送渠道,也要扩展很多类。

装饰器模式解决的是:

不通过修改原类,也不通过继承爆炸,而是通过对象包装来动态叠加能力。

chapter 5:案例背景:消息发送器增强

下面用一个 Java 后端开发中非常常见的案例:消息发送器。

系统中有一个统一消息发送接口:

1
MessageSender

核心发送器有:

  • 短信发送器;
  • 邮件发送器。

现在需要给发送器动态叠加以下能力:

  1. 日志记录;
  2. 失败重试;
  3. 限流;
  4. 耗时统计。

要求:

  • 不修改原始发送器;
  • 不为每种组合创建子类;
  • 增强能力可以自由组合;
  • 客户端仍然只依赖 MessageSender 接口。

这个场景非常适合装饰器模式。

chapter 6:定义抽象组件 MessageSender

先定义统一接口。

1
2
3
4
public interface MessageSender {

void send(String receiver, String content);
}

这是装饰器模式中的 Component。

不管是原始发送器,还是装饰器,都实现这个接口。

chapter 7:定义具体组件

1. 短信发送器

1
2
3
4
5
6
7
public class SmsMessageSender implements MessageSender {

@Override
public void send(String receiver, String content) {
System.out.println("发送短信给 " + receiver + ",内容:" + content);
}
}

2. 邮件发送器

1
2
3
4
5
6
7
public class EmailMessageSender implements MessageSender {

@Override
public void send(String receiver, String content) {
System.out.println("发送邮件给 " + receiver + ",内容:" + content);
}
}

这两个类只负责核心发送逻辑。

它们不关心日志、重试、限流、监控。

这很重要。

核心类越干净,系统越好维护。

chapter 8:定义抽象装饰器

抽象装饰器也实现 MessageSender。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public abstract class MessageSenderDecorator implements MessageSender {

protected final MessageSender delegate;

protected MessageSenderDecorator(MessageSender delegate) {
if (delegate == null) {
throw new IllegalArgumentException("delegate can not be null");
}

this.delegate = delegate;
}

@Override
public void send(String receiver, String content) {
delegate.send(receiver, content);
}
}

这里的关键字段是:

1
protected final MessageSender delegate;

装饰器内部持有另一个 MessageSender。

这个 delegate 可以是:

  • 原始发送器;
  • 另一个装饰器。

所以装饰器可以一层一层包起来。

chapter 9:日志装饰器

日志装饰器负责在发送前后打印日志。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class LoggingMessageSenderDecorator extends MessageSenderDecorator {

public LoggingMessageSenderDecorator(MessageSender delegate) {
super(delegate);
}

@Override
public void send(String receiver, String content) {
System.out.println("[LOG] 准备发送消息,receiver = " + receiver);

try {
delegate.send(receiver, content);

System.out.println("[LOG] 消息发送成功,receiver = " + receiver);
} catch (RuntimeException e) {
System.out.println("[LOG] 消息发送失败,receiver = " + receiver + ",error = " + e.getMessage());
throw e;
}
}
}

日志装饰器不关心底层到底是短信还是邮件。

只要被包装对象实现了 MessageSender,它就能增强。

chapter 10:耗时统计装饰器

耗时统计装饰器负责记录发送耗时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MetricsMessageSenderDecorator extends MessageSenderDecorator {

public MetricsMessageSenderDecorator(MessageSender delegate) {
super(delegate);
}

@Override
public void send(String receiver, String content) {
long start = System.currentTimeMillis();

try {
delegate.send(receiver, content);
} finally {
long cost = System.currentTimeMillis() - start;

System.out.println("[METRICS] 消息发送耗时:" + cost + " ms");
}
}
}

这个装饰器使用 finally,保证无论成功还是失败都会统计耗时。

真实项目中可以把这里替换成 Micrometer 指标:

1
timer.record(...)

chapter 11:重试装饰器

重试装饰器负责在发送失败时自动重试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class RetryMessageSenderDecorator extends MessageSenderDecorator {

private final int maxRetryTimes;

public RetryMessageSenderDecorator(MessageSender delegate, int maxRetryTimes) {
super(delegate);

if (maxRetryTimes < 0) {
throw new IllegalArgumentException("maxRetryTimes can not be negative");
}

this.maxRetryTimes = maxRetryTimes;
}

@Override
public void send(String receiver, String content) {
int attempt = 0;

while (true) {
try {
attempt++;

delegate.send(receiver, content);

return;
} catch (RuntimeException e) {
if (attempt > maxRetryTimes) {
System.out.println("[RETRY] 已达到最大重试次数,attempt = " + attempt);
throw e;
}

System.out.println("[RETRY] 消息发送失败,准备重试,attempt = " + attempt);
}
}
}
}

这里要注意一个边界:

1
attempt > maxRetryTimes

如果 maxRetryTimes = 3,通常表示失败后最多重试 3 次。

总调用次数是 1 次初始调用 + 3 次重试。

真实项目中还需要考虑:

  • 重试间隔;
  • 指数退避;
  • 只对特定异常重试;
  • 幂等性;
  • 是否会重复发送消息。

重试不是万能药。

在支付、下单、扣库存场景中,盲目重试可能比失败更危险。

chapter 12:限流装饰器

为了简单演示,这里写一个基于计数的简化限流器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.util.concurrent.Semaphore;

public class RateLimitMessageSenderDecorator extends MessageSenderDecorator {

private final Semaphore semaphore;

public RateLimitMessageSenderDecorator(MessageSender delegate, int maxConcurrent) {
super(delegate);

if (maxConcurrent <= 0) {
throw new IllegalArgumentException("maxConcurrent must be greater than 0");
}

this.semaphore = new Semaphore(maxConcurrent);
}

@Override
public void send(String receiver, String content) {
boolean acquired = semaphore.tryAcquire();

if (!acquired) {
throw new RuntimeException("Too many concurrent message send requests");
}

try {
delegate.send(receiver, content);
} finally {
semaphore.release();
}
}
}

这个装饰器限制并发发送数量。

真实项目中可以替换成:

  • Guava RateLimiter;
  • Sentinel;
  • Resilience4j RateLimiter;
  • Redis 分布式限流;
  • 自研滑动窗口限流。

装饰器模式并不关心限流算法是什么。

它只负责把限流能力包在原对象外面。

chapter 13:客户端组合装饰器

现在可以自由组合这些增强能力。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class DecoratorDemo {

public static void main(String[] args) {
MessageSender sender = new SmsMessageSender();

sender = new RateLimitMessageSenderDecorator(sender, 10);
sender = new RetryMessageSenderDecorator(sender, 3);
sender = new MetricsMessageSenderDecorator(sender);
sender = new LoggingMessageSenderDecorator(sender);

sender.send("13800000000", "您的验证码是 9527");
}
}

调用链如下:

1
2
3
4
5
Logging
└── Metrics
└── Retry
└── RateLimit
└── SmsMessageSender

执行顺序是:

1
2
3
4
5
6
7
8
9
Logging 前置逻辑
Metrics 开始计时
Retry 开始尝试
RateLimit 获取许可
SmsMessageSender 发送短信
RateLimit 释放许可
Retry 成功返回
Metrics 统计耗时
Logging 后置逻辑

可以看到,装饰器的顺序会影响执行效果。

例如:

1
2
sender = new RetryMessageSenderDecorator(sender, 3);
sender = new MetricsMessageSenderDecorator(sender);

表示统计的是“整体重试后的总耗时”。

如果顺序反过来:

1
2
sender = new MetricsMessageSenderDecorator(sender);
sender = new RetryMessageSenderDecorator(sender, 3);

表示每次重试都会单独统计耗时。

这就是装饰器模式非常重要的点:

装饰器不仅可以自由组合,组合顺序也有语义。

chapter 14:装饰器顺序很重要

装饰器模式最容易被忽略的问题就是顺序。

例如日志和重试。

1. 日志包在重试外面

1
2
3
4
5
6
MessageSender sender = new LoggingMessageSenderDecorator(
new RetryMessageSenderDecorator(
new SmsMessageSender(),
3
)
);

效果是:

  • 日志记录一次整体发送;
  • 重试过程在内部发生;
  • 如果最终成功,日志显示成功;
  • 如果最终失败,日志显示失败。

2. 重试包在日志外面

1
2
3
4
5
6
MessageSender sender = new RetryMessageSenderDecorator(
new LoggingMessageSenderDecorator(
new SmsMessageSender()
),
3
);

效果是:

  • 每一次尝试都会打印日志;
  • 如果失败重试 3 次,日志可能打印多次失败;
  • 更适合排查每次调用细节。

没有绝对好坏,只有语义不同。

所以在真实项目中,装饰器链一定要设计清楚。

否则线上看日志时会怀疑人生:为什么明明调用一次,日志出现了四遍?不是系统闹鬼,是装饰器在重试。

chapter 15:JDK 中的装饰器模式:IO 流

Java JDK 中最经典的装饰器模式就是 IO 流。

例如:

1
2
3
BufferedInputStream inputStream = new BufferedInputStream(
new FileInputStream("demo.txt")
);

这里:

  • InputStream 是抽象组件;
  • FileInputStream 是具体组件;
  • FilterInputStream 是抽象装饰器;
  • BufferedInputStream 是具体装饰器。

再比如:

1
2
3
4
5
DataInputStream inputStream = new DataInputStream(
new BufferedInputStream(
new FileInputStream("demo.txt")
)
);

这就是一层一层增强:

1
2
3
DataInputStream
└── BufferedInputStream
└── FileInputStream

FileInputStream 负责从文件读字节。

BufferedInputStream 增加缓冲能力。

DataInputStream 增加读取 Java 基本类型的能力。

这就是装饰器模式最经典的落地。

chapter 16:Spring 中的装饰器思想

Spring 中也有很多装饰器思想。

例如:

1. BeanPostProcessor

可以在 Bean 初始化前后增强 Bean。

2. HttpServletRequestWrapper

Servlet API 中的 HttpServletRequestWrapper 是典型装饰器。

它包装一个 HttpServletRequest,并允许子类重写部分方法。

1
2
3
4
5
6
public class CustomRequestWrapper extends HttpServletRequestWrapper {

public CustomRequestWrapper(HttpServletRequest request) {
super(request);
}
}

3. ResponseEntity 装配

虽然不是严格装饰器,但很多响应增强、包装、转换都体现了“在原对象外面加一层”的思想。

4. Spring Security Filter Chain

过滤器链和装饰器模式不完全相同,但都体现了对请求处理能力的层层增强。

设计模式不是只有“教科书式长相”才算,真实框架里经常是模式思想的变体。

chapter 17:在 Spring Boot 中落地装饰器

在 Spring Boot 项目中,装饰器可以通过 Bean 组合实现。

假设我们有一个核心发送器:

1
2
3
4
5
6
7
8
9
10
import org.springframework.stereotype.Component;

@Component
public class SmsMessageSender implements MessageSender {

@Override
public void send(String receiver, String content) {
System.out.println("发送短信给 " + receiver + ",内容:" + content);
}
}

如果直接加多个 @Component 装饰器,会遇到一个问题:

Spring 不知道注入哪个 MessageSender。

所以真实项目里通常有几种做法。

chapter 18:Spring 方式一:手动配置装饰器链

可以使用 @Configuration 显式装配。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MessageSenderConfig {

@Bean
public MessageSender messageSender() {
MessageSender sender = new SmsMessageSender();

sender = new RateLimitMessageSenderDecorator(sender, 10);
sender = new RetryMessageSenderDecorator(sender, 3);
sender = new MetricsMessageSenderDecorator(sender);
sender = new LoggingMessageSenderDecorator(sender);

return sender;
}
}

业务服务直接注入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.springframework.stereotype.Service;

@Service
public class NotificationService {

private final MessageSender messageSender;

public NotificationService(MessageSender messageSender) {
this.messageSender = messageSender;
}

public void sendVerifyCode(String phone, String code) {
messageSender.send(phone, "您的验证码是:" + code);
}
}

这种方式简单直观。

缺点是装饰器链写死在配置类里。

chapter 19:Spring 方式二:核心 Bean 和装饰器 Bean 分开命名

也可以保留核心 Bean,然后定义一个增强后的 Bean。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MessageSenderConfig {

@Bean
public MessageSender rawSmsMessageSender() {
return new SmsMessageSender();
}

@Bean
public MessageSender decoratedMessageSender() {
MessageSender sender = rawSmsMessageSender();

sender = new RateLimitMessageSenderDecorator(sender, 10);
sender = new RetryMessageSenderDecorator(sender, 3);
sender = new MetricsMessageSenderDecorator(sender);
sender = new LoggingMessageSenderDecorator(sender);

return sender;
}
}

业务侧使用 @Qualifier 指定注入增强后的 Bean。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class NotificationService {

private final MessageSender messageSender;

public NotificationService(@Qualifier("decoratedMessageSender") MessageSender messageSender) {
this.messageSender = messageSender;
}

public void send(String receiver, String content) {
messageSender.send(receiver, content);
}
}

这种方式比较清晰:

  • rawSmsMessageSender 是原始对象;
  • decoratedMessageSender 是装饰后的对象。

chapter 20:Spring 方式三:@Primary 指定装饰后对象

如果希望业务默认使用装饰后的对象,可以使用 @Primary。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration
public class MessageSenderConfig {

@Bean
public MessageSender rawSmsMessageSender() {
return new SmsMessageSender();
}

@Bean
@Primary
public MessageSender decoratedMessageSender(MessageSender rawSmsMessageSender) {
MessageSender sender = rawSmsMessageSender;

sender = new RateLimitMessageSenderDecorator(sender, 10);
sender = new RetryMessageSenderDecorator(sender, 3);
sender = new MetricsMessageSenderDecorator(sender);
sender = new LoggingMessageSenderDecorator(sender);

return sender;
}
}

业务服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.springframework.stereotype.Service;

@Service
public class NotificationService {

private final MessageSender messageSender;

public NotificationService(MessageSender messageSender) {
this.messageSender = messageSender;
}

public void send(String receiver, String content) {
messageSender.send(receiver, content);
}
}

这样默认注入的是 @Primary 的装饰后对象。

不过要注意避免循环依赖或注入歧义。

chapter 21:Spring 方式四:使用 AOP 是否也可以

很多增强能力也可以用 AOP 实现,例如:

  • 日志;
  • 耗时统计;
  • 权限校验;
  • 事务;
  • 监控埋点。

那装饰器和 AOP 怎么选?

可以简单理解:

装饰器适合对象级、显式、可组合的增强。

AOP 适合横切关注点、规则统一、透明增强。

例如:

  • 给某一个 MessageSender 自由组合重试、限流、日志,用装饰器很合适;
  • 给所有 @Service 方法统一打印耗时日志,用 AOP 更合适;
  • 给所有数据库操作加事务,用 Spring AOP 更合适;
  • 给不同渠道发送器配置不同增强链,用装饰器更合适。

两者不是对立关系。

真实项目里经常一起使用。

chapter 22:装饰器模式和代理模式的区别

装饰器模式和代理模式很容易混。

它们都包装一个对象,也都实现相同接口。

但目的不同。

对比项 装饰器模式 代理模式
目的 动态增强对象功能 控制对对象的访问
关注点 添加职责 访问控制
是否强调组合多个增强 是 不一定
客户端是否知道被增强 可以知道 通常不关心
典型场景 日志、重试、限流、缓冲 远程代理、权限代理、懒加载代理
示例 BufferedInputStream 包装 FileInputStream MyBatis Mapper 代理、Spring AOP 代理

一句话区分:

装饰器是为了增强功能。

代理是为了控制访问。

例如:

1
new LoggingMessageSenderDecorator(new SmsMessageSender())

更像装饰器。

Spring AOP 生成代理对象,对方法调用做拦截,更像代理模式。

当然,真实框架中二者思想有时会重叠,不用过度纠结名字。

chapter 23:装饰器模式和适配器模式的区别

适配器模式也是包装对象,但目的不同。

对比项 装饰器模式 适配器模式
目的 增强原有功能 转换不兼容接口
接口是否一致 一致 通常不一致
关注点 加功能 做接口转换
示例 给 SmsSender 加日志 把 AliPaySdk 适配成 PaymentClient

装饰器说:

你这个接口我能用,我只是给你加点功能。

适配器说:

你这个接口我不能直接用,我得给你转一下。

chapter 24:装饰器模式和组合模式的区别

装饰器模式和组合模式都用了组合关系,但结构目的不同。

对比项 装饰器模式 组合模式
目的 动态增强对象功能 表达树形整体-部分关系
内部对象数量 通常包装一个对象 通常包含多个子节点
结构 链式包裹 树形结构
示例 日志装饰器包短信发送器 菜单包含子菜单和按钮

装饰器是一层包一层。

组合模式是一棵树。

二者都用了“组合”,但不是一回事。

chapter 25:装饰器模式和桥接模式的区别

桥接模式是拆分两个独立变化维度。

装饰器模式是给对象动态叠加功能。

对比项 装饰器模式 桥接模式
目的 动态增强功能 分离抽象和实现
结构 同接口对象互相包装 抽象层持有实现层
关注点 功能叠加 维度拆分
示例 给 MessageSender 加重试 Message 类型组合 Sender 渠道

桥接模式解决:

1
消息类型 × 发送渠道

装饰器模式解决:

1
发送渠道 + 日志 + 重试 + 限流

一个是横向拆维度,一个是纵向叠功能。

chapter 26:装饰器模式的优点

1. 符合开闭原则

新增增强功能时,不需要修改原类。

新增一个装饰器即可。

2. 比继承更灵活

装饰器可以在运行时组合。

继承关系在编译期就固定了。

3. 可以自由组合功能

例如:

1
2
3
日志 + 重试
限流 + 指标
日志 + 限流 + 重试 + 指标

组合方式灵活。

4. 单一职责更清晰

核心对象只负责核心功能。

装饰器分别负责日志、重试、限流、监控。

5. 可以多层嵌套

装饰器可以包装另一个装饰器,从而形成增强链。

chapter 27:装饰器模式的缺点

1. 类数量增加

每一种增强能力通常对应一个装饰器类。

2. 调用链变长

对象被多层包装后,调试时调用链会更长。

3. 装饰顺序影响行为

不同顺序可能产生不同结果。

这需要开发者明确设计。

4. 过度使用会让结构复杂

如果装饰器太多,代码可能变成:

1
new A(new B(new C(new D(new E(target)))))

看起来像俄罗斯套娃开会。

可以通过配置类、Builder 或工厂来管理装饰器链。

5. 状态管理要谨慎

装饰器如果内部有可变状态,要考虑线程安全。

尤其是在 Spring 单例 Bean 场景中。

chapter 28:适用场景

装饰器模式适合以下场景。

1. 希望在不修改原类的情况下增强功能

例如:

  • 日志;
  • 监控;
  • 限流;
  • 重试;
  • 缓存;
  • 压缩;
  • 加密;
  • 权限校验。

2. 增强功能可以自由组合

例如某些接口需要日志和重试,某些接口只需要日志,某些接口需要限流和监控。

3. 不希望通过继承扩展功能

如果继承会导致类爆炸,装饰器更合适。

4. 原类不能修改

例如 JDK 类、第三方类、历史系统类。

5. 功能是附加职责,而不是核心职责

例如发送消息的核心职责是发送。

日志、重试、限流是附加职责。

chapter 29:不适合使用的场景

以下场景不建议使用装饰器模式。

1. 只是简单增强一次

如果只有一个非常简单的增强,直接修改原类可能更简单。

2. 增强逻辑强依赖对象内部状态

装饰器通常通过接口和公开方法增强对象。

如果增强逻辑需要大量访问对象内部私有状态,装饰器不一定合适。

3. 增强顺序非常复杂且难以理解

如果装饰器顺序多到难以维护,可能需要重新设计流程。

4. 核心对象接口过大

装饰器需要实现同一个接口。

如果接口方法很多,每个装饰器都要处理很多方法,代码会很重。

5. 横切逻辑覆盖范围很广

例如全系统所有方法统一日志,这种用 AOP 更合适。

chapter 30:真实项目中的实践建议

1. 装饰器名称要体现增强能力

推荐命名:

1
2
3
4
LoggingMessageSenderDecorator
RetryMessageSenderDecorator
RateLimitMessageSenderDecorator
MetricsMessageSenderDecorator

看到类名就知道它加了什么能力。

2. 抽象装饰器不要塞太多逻辑

抽象装饰器只做委托即可。

具体增强逻辑放到具体装饰器中。

3. 注意装饰器顺序

日志、重试、指标、限流的顺序要设计清楚。

推荐用配置类统一组装,避免业务代码到处手写装饰链。

4. 装饰器里不要写核心业务

装饰器适合附加职责。

不要把订单创建、库存扣减、支付状态流转写进装饰器。

否则装饰器会变成隐形业务流程,排查问题非常痛苦。

5. Spring 单例装饰器注意线程安全

装饰器如果作为 Spring Bean 单例存在,内部不要保存请求级状态。

例如不要这样:

1
private String currentReceiver;

请求级数据应该放在方法参数、局部变量或上下文中。

6. 可组合增强适合装饰器,统一横切增强适合 AOP

选择建议:

场景 推荐方式
给某个对象自由组合增强 装饰器
全局统一日志 AOP
全局事务 AOP
JDK IO 缓冲/压缩 装饰器
第三方接口转换 适配器
远程调用代理 代理模式

7. 可以用工厂封装装饰器链

如果装饰器很多,不建议在业务代码中直接套娃。

可以写一个工厂:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MessageSenderFactory {

public static MessageSender createSmsSender() {
MessageSender sender = new SmsMessageSender();

sender = new RateLimitMessageSenderDecorator(sender, 10);
sender = new RetryMessageSenderDecorator(sender, 3);
sender = new MetricsMessageSenderDecorator(sender);
sender = new LoggingMessageSenderDecorator(sender);

return sender;
}
}

业务代码只调用:

1
MessageSender sender = MessageSenderFactory.createSmsSender();

chapter 31:完整案例代码汇总

抽象组件

1
2
3
4
public interface MessageSender {

void send(String receiver, String content);
}

具体组件

1
2
3
4
5
6
7
public class SmsMessageSender implements MessageSender {

@Override
public void send(String receiver, String content) {
System.out.println("发送短信给 " + receiver + ",内容:" + content);
}
}
1
2
3
4
5
6
7
public class EmailMessageSender implements MessageSender {

@Override
public void send(String receiver, String content) {
System.out.println("发送邮件给 " + receiver + ",内容:" + content);
}
}

抽象装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public abstract class MessageSenderDecorator implements MessageSender {

protected final MessageSender delegate;

protected MessageSenderDecorator(MessageSender delegate) {
if (delegate == null) {
throw new IllegalArgumentException("delegate can not be null");
}

this.delegate = delegate;
}

@Override
public void send(String receiver, String content) {
delegate.send(receiver, content);
}
}

日志装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class LoggingMessageSenderDecorator extends MessageSenderDecorator {

public LoggingMessageSenderDecorator(MessageSender delegate) {
super(delegate);
}

@Override
public void send(String receiver, String content) {
System.out.println("[LOG] 准备发送消息,receiver = " + receiver);

try {
delegate.send(receiver, content);

System.out.println("[LOG] 消息发送成功,receiver = " + receiver);
} catch (RuntimeException e) {
System.out.println("[LOG] 消息发送失败,receiver = " + receiver + ",error = " + e.getMessage());
throw e;
}
}
}

指标装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MetricsMessageSenderDecorator extends MessageSenderDecorator {

public MetricsMessageSenderDecorator(MessageSender delegate) {
super(delegate);
}

@Override
public void send(String receiver, String content) {
long start = System.currentTimeMillis();

try {
delegate.send(receiver, content);
} finally {
long cost = System.currentTimeMillis() - start;

System.out.println("[METRICS] 消息发送耗时:" + cost + " ms");
}
}
}

重试装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class RetryMessageSenderDecorator extends MessageSenderDecorator {

private final int maxRetryTimes;

public RetryMessageSenderDecorator(MessageSender delegate, int maxRetryTimes) {
super(delegate);

if (maxRetryTimes < 0) {
throw new IllegalArgumentException("maxRetryTimes can not be negative");
}

this.maxRetryTimes = maxRetryTimes;
}

@Override
public void send(String receiver, String content) {
int attempt = 0;

while (true) {
try {
attempt++;

delegate.send(receiver, content);

return;
} catch (RuntimeException e) {
if (attempt > maxRetryTimes) {
System.out.println("[RETRY] 已达到最大重试次数,attempt = " + attempt);
throw e;
}

System.out.println("[RETRY] 消息发送失败,准备重试,attempt = " + attempt);
}
}
}
}

限流装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.util.concurrent.Semaphore;

public class RateLimitMessageSenderDecorator extends MessageSenderDecorator {

private final Semaphore semaphore;

public RateLimitMessageSenderDecorator(MessageSender delegate, int maxConcurrent) {
super(delegate);

if (maxConcurrent <= 0) {
throw new IllegalArgumentException("maxConcurrent must be greater than 0");
}

this.semaphore = new Semaphore(maxConcurrent);
}

@Override
public void send(String receiver, String content) {
boolean acquired = semaphore.tryAcquire();

if (!acquired) {
throw new RuntimeException("Too many concurrent message send requests");
}

try {
delegate.send(receiver, content);
} finally {
semaphore.release();
}
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
public class DecoratorDemo {

public static void main(String[] args) {
MessageSender sender = new SmsMessageSender();

sender = new RateLimitMessageSenderDecorator(sender, 10);
sender = new RetryMessageSenderDecorator(sender, 3);
sender = new MetricsMessageSenderDecorator(sender);
sender = new LoggingMessageSenderDecorator(sender);

sender.send("13800000000", "您的验证码是 9527");
}
}

chapter 32:一句话总结

装饰器模式的本质是:

在不修改原对象的前提下,通过包装对象的方式动态叠加额外功能。

它特别适合:

  • 日志增强;
  • 重试增强;
  • 限流增强;
  • 缓存增强;
  • 监控增强;
  • 加密压缩;
  • IO 流增强;
  • 对已有对象增加附加职责。

装饰器模式解决的不是“接口不兼容”,那是适配器模式。

它解决的是:

原对象能用,但我还想给它加点能力。

在 Java 后端开发中,装饰器模式最适合那些可组合、可插拔、非核心业务的增强逻辑。

好的装饰器像一层轻甲:增强能力,但不改变本体。

坏的装饰器像一层层胶带:越缠越厚,最后谁也不知道里面包的是什么。

参考资料

  • Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software.
  • Robert C. Martin. Agile Software Development, Principles, Patterns, and Practices.
  • Joshua Bloch. Effective Java.
  • Oracle Java Documentation: java.io InputStream and FilterInputStream.
  • Spring Framework Documentation: Core Technologies - The IoC Container.
  • Refactoring Guru: Decorator Pattern.
  • SourceMaking: Decorator Design Pattern.

启示录

富贵岂由人,时会高志须酬。

能成功于千载者,必以近察远。


设计模式:装饰器模式
https://allendericdalexander.github.io/2026/04/01/java/design/10decorator-pattern-blog/
作者
AtLuoFu
发布于
2026年4月1日
许可协议