Skip to content

第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
任务队列
异步执行
撤销/重做
审计日志
批处理任务

在企业系统中,很多 RequestCommandJob 对象都带有命令模式影子。


📖 七、状态模式

订单状态如果用大量 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 对比

维度CJava
策略函数指针表接口 + 多实现
观察者回调函数列表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 比模式更好?