首页/开发/代码质量与评审速查/状态模式 State(行为型)
设计原则与模式62 条

状态模式 State(行为型)

对象行为随内部状态改变:把每个状态封装为类,状态迁移显式化;相比状态字段 + switch,状态类让「迁移表」可读、单状态逻辑可测。

状态模式是什么

对象的行为随内部状态改变时,把每个状态封装成类,并把状态迁移显式化:当前状态对象负责处理事件并返回下一个状态。相比一个状态字段加一堆 switch,状态类让迁移关系聚集、单个状态的逻辑可独立测试,新增状态也不必改遍所有分支。

基本信息

意图行为随状态变化,迁移关系显式
对比枚举加 switch:分支散落,迁移难以追踪
迁移方式状态对象处理事件后返回下一个状态
配套与状态机表、流程图互相印证

典型使用场景

  • 订单状态:草稿、已提交、已支付、已发货、已取消,各自可做的操作不同
  • 连接状态:未连接、连接中、已连接、断开中,重试策略随状态不同
  • 文档审批:草稿、待审、已批准、已驳回
  • 任务调度:待运行、运行中、重试中、完成、失败
  • 把合法迁移集中到一处,非法操作给出明确错误而不是静默失败

形态示意

形态示意
interface OrderState {
    OrderState pay(Order o);
    OrderState ship(Order o);
    default OrderState cancel(Order o) {
        throw new IllegalStateException("当前状态不可取消");
    }
}

class Order {
    private OrderState state = new DraftState();
    void pay()  { state = state.pay(this); }
    void ship() { state = state.ship(this); }
}

状态类与枚举加分支的对比

维度状态类枚举加 switch
迁移可见性集中且可画图分散在各方法
新增状态新增类与迁移每个 switch 都要改
单状态测试可直接构造状态对象需组合出前置状态
类数量随状态增长恒定但分支膨胀
状态对象直接 new 下一个状态会让类之间强耦合,迁移关系难以集中查看。可行的做法是由状态机工厂或迁移表决定下一状态,状态类只表达「当前可否执行该操作」。
并发场景下状态对象应尽量无实例状态,或保证共享实例不可变:同一个状态实例被多个订单共用时,若它保存了可变数据,行为会互相干扰。

易错点

  • 状态类里直接 new 目标状态:类之间强耦合,迁移图难以集中维护
  • 并发使用同一状态实例:状态对象应尽量无实例状态或保持不可变
  • 非法操作静默忽略:调用方以为成功,实际状态没变,问题延后暴露
  • 状态与业务数据混在一起:状态类持有订单字段,测试需要构造大量无关数据
  • 迁移关系只存在于代码里:没有状态图或迁移表可对照,评审时无法确认合法性

数据来源与参考

  • 《设计模式》(GoF)· State
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌