状态模式是什么
对象的行为随内部状态改变时,把每个状态封装成类,并把状态迁移显式化:当前状态对象负责处理事件并返回下一个状态。相比一个状态字段加一堆 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 下一个状态会让类之间强耦合,迁移关系难以集中查看。可行的做法是由状态机工厂或迁移表决定下一状态,状态类只表达「当前可否执行该操作」。
并发场景下状态对象应尽量无实例状态,或保证共享实例不可变:同一个状态实例被多个订单共用时,若它保存了可变数据,行为会互相干扰。