Skip to content

行为型模式总览 ​

行为型模式关注对象之间如何协作。它们解决的是算法替换、流程扩展、状态切换、事件通知、请求分发、撤销恢复等行为变化问题。

行为型模式总览

解决的核心问题 ​

行为型模式常出现在这些信号里:

  • 一段逻辑里有大量 if else 或 switch,分支还会继续增加。
  • 算法经常切换,但调用方不应该关心算法细节。
  • 流程大体固定,但某些步骤由不同子类或扩展点实现。
  • 状态变化后,对象行为完全不同。
  • 一个事件发生后,需要通知多个对象。
  • 一个请求可能由多个处理器中的某一个处理。
  • 操作需要排队、记录、撤销、重做。
  • 数据结构稳定,但针对结构的操作不断增加。

行为型模式的共同目标是把协作逻辑从硬编码调用中拆出来,让职责更清晰。

十一种行为型模式 ​

模式适合场景直观理解
策略算法可替换同一目标,多种打法
模板方法流程骨架固定,步骤可变固定流程,开放步骤
观察者状态变化通知多个对象发布订阅
迭代器统一遍历集合不暴露内部结构也能遍历
责任链多个处理器依次尝试处理请求审批流
状态状态不同,行为不同订单状态机
命令请求需要对象化可排队、可记录、可撤销的操作
备忘录保存和恢复状态快照与撤销
解释器解释简单规则或表达式小型规则引擎
访问者数据结构稳定,操作常变化给结构新增操作
中介者多对象交互复杂用协调中心减少互相依赖

怎么选 ​

算法可替换 ​

选择策略模式。比如不同折扣算法、路由算法、排序规则、支付费率计算。策略强调“同一件事有多种做法”。

流程固定,步骤可变 ​

选择模板方法。比如导入流程固定为读取、校验、转换、保存,但每类文件的读取和转换不同。

状态决定行为 ​

选择状态模式。比如订单、工单、审批单、连接对象。状态模式适合把状态分支从主类中拆出去。

一个事件通知多个对象 ​

选择观察者模式。比如事件总线、监听器、消息订阅、模型视图同步。

请求需要一站站处理 ​

选择责任链。比如过滤器链、拦截器链、审批链、风控规则链。

操作需要记录和撤销 ​

选择命令模式,必要时配合备忘录。命令保存“要做什么”,备忘录保存“做之前是什么状态”。

多对象互相调用太乱 ​

选择中介者。它把复杂网状交互收敛到协调中心,适合 UI 控件联动、聊天室、工作流协调。

数据结构稳定,操作增加快 ​

选择访问者。比如 AST、报表结构、组织结构,需要不断增加导出、统计、校验等操作。

实操:行为变化怎么拆 ​

场景一:促销计算从分支改成策略 ​

问题代码:

java
public BigDecimal calculate(String promotionType, Order order) {
    if ("discount".equals(promotionType)) {
        return order.getAmount().multiply(new BigDecimal("0.9"));
    }
    if ("full_reduce".equals(promotionType)) {
        return order.getAmount().subtract(new BigDecimal("20"));
    }
    if ("coupon".equals(promotionType)) {
        return couponService.calculate(order);
    }
    return order.getAmount();
}

当促销规则频繁变化时,可以拆成策略:

java
public interface PromotionStrategy {
    String type();
    BigDecimal calculate(Order order);
}

public class PromotionService {
    private final Map<String, PromotionStrategy> strategies;

    public BigDecimal calculate(String type, Order order) {
        PromotionStrategy strategy = strategies.get(type);
        if (strategy == null) {
            return order.getAmount();
        }
        return strategy.calculate(order);
    }
}

新增促销时新增一个策略类,不修改原有计算服务。

场景二:校验流程改成责任链 ​

问题代码:

java
public void submit(Order order) {
    checkUser(order);
    checkAddress(order);
    checkInventory(order);
    checkRisk(order);
    createOrder(order);
}

如果校验节点会按业务线增减,可以拆成处理器链:

java
public interface OrderValidator {
    void validate(Order order);
}

public class OrderSubmitService {
    private final List<OrderValidator> validators;

    public void submit(Order order) {
        for (OrderValidator validator : validators) {
            validator.validate(order);
        }
        createOrder(order);
    }
}

责任链适合处理节点可插拔的流程。链路顺序本身是业务规则,需要明确配置和测试。

场景三:订单状态从条件判断改成状态模式 ​

问题代码:

java
public void cancel(Order order) {
    if (order.isPaid()) {
        refund(order);
        order.setStatus("CANCELLED");
    } else if (order.isCreated()) {
        order.setStatus("CANCELLED");
    } else if (order.isShipped()) {
        throw new IllegalStateException("已发货订单不能直接取消");
    }
}

状态越来越多时,可以把状态行为拆出去:

java
public interface OrderState {
    void cancel(Order order);
}

public class PaidState implements OrderState {
    public void cancel(Order order) {
        refund(order);
        order.changeState(new CancelledState());
    }
}

public class ShippedState implements OrderState {
    public void cancel(Order order) {
        throw new IllegalStateException("已发货订单不能直接取消");
    }
}

状态模式适合状态数量较多、状态行为差异明显的场景。如果只有两个状态且变化很少,普通分支更简单。

容易混淆的模式 ​

模式相似点区分方式
策略 vs 状态都把行为拆到独立类策略由外部选择,状态由对象内部状态驱动
策略 vs 模板方法都能改变算法策略用组合替换整体算法,模板方法用继承替换局部步骤
责任链 vs 命令都能封装请求责任链强调谁处理,命令强调请求本身可保存和调度
观察者 vs 中介者都降低对象直接依赖观察者是一对多通知,中介者是多对象交互协调
备忘录 vs 命令都可用于撤销命令记录操作,备忘录记录状态

常见误区 ​

  • 策略类过多但变化很少,会造成过度设计。
  • 观察者链路过长时,问题定位会变难,需要日志和事件边界。
  • 责任链处理顺序很重要,顺序隐含业务规则时要明确文档化。
  • 状态模式不适合状态很少且稳定的简单分支。
  • 访问者要求数据结构稳定,如果元素类型频繁变化会很痛苦。
  • 解释器只适合小语言或规则表达式,复杂语言应使用成熟解析工具。

行为型模式最重要的是让协作可见。读代码时应该能看出请求如何流动、状态如何切换、扩展点在哪里。

别急,先让缓存热一下。