Appearance
第77课:行为型模式
🎯 学习目标
- 理解行为型模式解决的是“对象之间如何协作、职责如何分配”的问题。
- 掌握策略、模板方法、观察者、责任链、命令、状态、迭代器等常见模式。
- 能在 Java 和 Spring 项目中识别行为型模式的真实应用。
- 能使用模式消除大量
if-else、重复流程代码和耦合调用。 - 能判断行为型模式的边界,避免过度抽象。
📖 一、行为型模式解决什么问题
业务系统的复杂度往往来自行为变化:
text
不同支付方式有不同处理逻辑
不同订单状态允许不同操作
一个事件发生后要通知多个模块
请求要经过多个校验和处理节点
多个算法可以互换行为型模式关注对象如何分工协作,让变化点局部化。
📖 二、策略模式
策略模式把一组可替换算法封装起来。
错误示例:
java
public void pay(String type, PayCommand command) {
if ("ALIPAY".equals(type)) {
alipay(command);
} else if ("WECHAT".equals(type)) {
wechatPay(command);
} else if ("CARD".equals(type)) {
cardPay(command);
}
}策略接口:
java
public interface PayStrategy {
String type();
PayResult pay(PayCommand command);
}实现:
java
@Component
public class AlipayStrategy implements PayStrategy {
public String type() {
return "ALIPAY";
}
public PayResult pay(PayCommand command) {
return PayResult.success();
}
}路由:
java
@Service
public class PayService {
private final Map<String, PayStrategy> strategies;
public PayService(List<PayStrategy> strategyList) {
this.strategies = strategyList.stream()
.collect(Collectors.toMap(PayStrategy::type, Function.identity()));
}
public PayResult pay(String type, PayCommand command) {
PayStrategy strategy = strategies.get(type);
if (strategy == null) {
throw new IllegalArgumentException("unsupported pay type: " + type);
}
return strategy.pay(command);
}
}新增支付方式时,只新增一个策略类。
📖 三、模板方法模式
模板方法把固定流程放在父类,把可变步骤交给子类。
java
public abstract class ImportTemplate {
public final ImportResult importFile(File file) {
validate(file);
List<Row> rows = parse(file);
List<Row> cleaned = clean(rows);
return save(cleaned);
}
protected void validate(File file) {
if (!file.exists()) {
throw new IllegalArgumentException("file not found");
}
}
protected abstract List<Row> parse(File file);
protected List<Row> clean(List<Row> rows) {
return rows;
}
protected abstract ImportResult save(List<Row> rows);
}CSV 导入:
java
public class CsvImportService extends ImportTemplate {
protected List<Row> parse(File file) {
// 解析 CSV
}
protected ImportResult save(List<Row> rows) {
// 保存数据
}
}Spring 中很多生命周期方法也有模板思想,例如 JdbcTemplate 隐藏连接获取、异常转换和资源释放,只让你提供 SQL 和参数。
📖 四、观察者模式
观察者模式用于事件发生后通知多个订阅者。
Spring 事件示例:
java
public record OrderCreatedEvent(Long orderId, Long userId) {
}发布事件:
java
@Service
public class OrderService {
private final ApplicationEventPublisher publisher;
public void createOrder(OrderCommand command) {
Order order = orderRepository.save(command.toOrder());
publisher.publishEvent(new OrderCreatedEvent(order.getId(), order.getUserId()));
}
}监听事件:
java
@Component
public class CouponListener {
@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
couponService.freezeCoupon(event.orderId());
}
}适合把主流程和旁路动作解耦:
text
下单成功 -> 发积分、发通知、写审计日志、更新推荐特征如果事件必须可靠投递,应该使用消息队列,而不是仅靠 JVM 内事件。
📖 五、责任链模式
责任链把请求交给多个处理器依次处理。
典型场景:
text
登录校验
风控校验
订单创建校验
网关过滤器
审批流程处理器接口:
java
public interface OrderValidator {
void validate(OrderCommand command);
}实现:
java
@Component
@Order(1)
public class StockValidator implements OrderValidator {
public void validate(OrderCommand command) {
if (!stockService.hasStock(command.productId(), command.quantity())) {
throw new BusinessException("库存不足");
}
}
}执行链:
java
@Service
public class OrderValidationChain {
private final List<OrderValidator> validators;
public OrderValidationChain(List<OrderValidator> validators) {
this.validators = validators;
}
public void validate(OrderCommand command) {
for (OrderValidator validator : validators) {
validator.validate(command);
}
}
}Spring MVC Filter、Interceptor、Spring Security FilterChain 都体现了责任链思想。
📖 六、命令模式
命令模式把一次操作封装为对象。
java
public interface Command {
void execute();
}
public class CreateOrderCommand implements Command {
private final OrderService orderService;
private final OrderRequest request;
public void execute() {
orderService.create(request);
}
}适用场景:
text
任务队列
异步执行
撤销/重做
审计日志
批处理任务在企业系统中,很多 Request、Command、Job 对象都带有命令模式影子。
📖 七、状态模式
订单状态如果用大量 if-else,会越来越难维护:
java
if (status == CREATED) {
pay();
} else if (status == PAID) {
ship();
} else if (status == CANCELED) {
throw new IllegalStateException();
}状态接口:
java
public interface OrderState {
OrderStatus status();
void pay(Order order);
void cancel(Order order);
}已创建状态:
java
public class CreatedState implements OrderState {
public OrderStatus status() {
return OrderStatus.CREATED;
}
public void pay(Order order) {
order.markPaid();
}
public void cancel(Order order) {
order.markCanceled();
}
}状态模式适合状态多、状态转移规则复杂的业务。简单状态机用枚举和表驱动也可以。
📖 八、迭代器模式
迭代器隐藏集合内部结构,统一遍历方式。
java
Iterator<User> iterator = users.iterator();
while (iterator.hasNext()) {
User user = iterator.next();
}Java 集合框架大量使用迭代器。增强 for 本质上依赖 Iterable:
java
for (User user : users) {
// 编译器转换为 Iterator 遍历
}迭代器还可以用于分页遍历、流式读取大文件、游标读取数据库结果。
⚠️ 九、常见陷阱
1. 策略类太碎
只有两三个简单分支时,不一定需要策略模式。分支频繁变化或每个分支逻辑复杂时再拆。
2. 观察者异常影响主流程
事件监听器抛异常是否影响发布方,要明确设计。旁路任务可以异步并独立失败。
3. 责任链顺序不明确
校验链、过滤链必须有稳定顺序,否则行为不可预测。
4. 状态模式过度设计
状态很少、规则简单时,枚举方法或状态转移表更轻量。
🆚 十、Java vs C 对比
| 维度 | C | Java |
|---|---|---|
| 策略 | 函数指针表 | 接口 + 多实现 |
| 观察者 | 回调函数列表 | Listener、Event、消息队列 |
| 责任链 | 函数链表 | Filter、Interceptor、Handler |
| 状态 | switch + 函数指针 | enum、State 类、状态机 |
Java 的接口、多态、注解和容器能让行为型模式更容易被框架自动装配。
💡 十一、最佳实践
- 大量同类 if-else 且分支会变化时,优先考虑策略模式。
- 固定流程 + 可变步骤时,考虑模板方法。
- 主流程完成后触发旁路动作,考虑观察者或消息队列。
- 多个校验、过滤、处理节点,考虑责任链。
- 状态转换复杂时,显式建模状态,不要让状态规则散落在 Service 中。
- 模式应让代码更清晰;如果类数量增加但理解成本更高,就要重新评估。
🎓 小结
行为型模式关注“对象如何协作”。它们能把变化的算法、流程、事件、校验链和状态转移从杂乱的业务代码中抽出来,让系统更容易扩展和测试。
学习行为型模式时,重点不是记名字,而是识别业务中的变化点和职责边界。
🧭 十二、模式选择速查
| 问题 | 优先考虑 |
|---|---|
| 多种算法或处理方式可切换 | 策略 |
| 流程固定但部分步骤变化 | 模板方法 |
| 一个事件触发多个后续动作 | 观察者 |
| 多个处理器按顺序处理请求 | 责任链 |
| 操作需要排队、审计、撤销 | 命令 |
| 状态多且转移规则复杂 | 状态 |
| 统一遍历集合或流式数据 | 迭代器 |
不要一看到 if-else 就拆策略。真正需要策略模式的信号是:
text
分支数量持续增加
每个分支都有独立业务规则
分支需要独立测试
新增分支不希望修改主流程📌 十三、企业项目中的组合应用
真实业务往往不是单个模式,而是多个模式组合。
下单流程可能是:
text
责任链:参数、库存、风控、优惠券校验
策略:不同支付方式
状态:订单 CREATED、PAID、SHIPPED、CANCELED
观察者:下单成功后发消息、积分、通知
模板方法:不同导入任务共享固定流程这种组合不是为了炫技,而是让每个变化点有自己的位置。
如果所有逻辑都堆在 OrderService.createOrder(),短期写得快,长期会变成无法测试和无法扩展的上帝方法。
🔍 十四、自测问题
text
策略模式如何消除不断增长的 if-else?
模板方法和策略模式有什么区别?
Spring 事件适合替代消息队列吗?
责任链中处理器顺序如何保证?
状态模式适合什么样的订单场景?
命令模式为什么适合异步任务?
观察者监听器失败是否应该影响主流程?
什么时候简单 if-else 比模式更好?