设计模式:装饰器模式

欢迎你来读这篇博客,这篇博客主要是关于装饰器模式
其中包括装饰器模式的核心思想、适用场景、优缺点、与代理模式和适配器模式的区别,以及 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日
许可协议