状态模式和状态机有什么区别?
简化版
状态模式是一种面向对象设计模式,把不同状态下的行为封装到状态类中;状态机是一种更通用的建模方式,强调状态、事件、迁移和动作的完整规则。状态模式适合用代码组织状态行为,状态机更适合管理复杂、可视化、可校验的状态流转。
详细版
状态模式和状态机都处理状态变化,但侧重点不同。
状态模式关注代码结构:
- Context 持有当前 State;
- State 封装当前状态下的行为;
- 行为执行时可能切换状态。
状态机关注规则模型:
- 有哪些状态;
- 哪些事件触发迁移;
- 哪些迁移合法;
- 迁移前后执行什么动作;
- 是否需要守卫条件;
- 是否需要可视化和校验。
简单状态流程可以用状态模式;复杂业务流程,比如订单、工单、审批、交易状态,往往更适合显式状态机或状态机框架。状态模式可以作为状态机的一种实现方式,但两者不是同一个概念。
完整版教学
一、状态模式更像代码组织方式
状态模式解决的是代码里条件分支太多的问题。
比如:
待支付状态类
已支付状态类
已发货状态类
已完成状态类
每个状态类封装自己状态下的行为。
它强调的是用多态替代条件判断,让代码更符合面向对象设计。
二、状态机更像业务规则模型
状态机通常会明确描述:
当前状态 + 事件 + 条件 -> 下一个状态 + 动作
比如订单状态机:
| 当前状态 | 事件 | 下一个状态 | 动作 |
|---|---|---|---|
| 待支付 | 支付成功 | 已支付 | 记录支付单 |
| 待支付 | 用户取消 | 已取消 | 释放优惠券 |
| 已支付 | 商家发货 | 已发货 | 写物流单 |
| 已发货 | 确认收货 | 已完成 | 结算 |
这种表格比散落在多个状态类里的迁移关系更容易审查。
三、状态机强调事件驱动
状态模式里方法可能直接叫:
order.pay();
order.ship();
状态机里通常会抽象成事件:
PAY_SUCCESS
SHIP
CONFIRM_RECEIVE
CANCEL
状态迁移由事件触发。
这种事件模型更适合复杂业务,因为它能记录“为什么发生状态变化”,也方便做审计和重放。
四、状态机更适合复杂迁移校验
复杂系统里,状态迁移需要校验:
- 当前状态是否允许该事件;
- 事件参数是否合法;
- 是否满足守卫条件;
- 是否需要并发控制;
- 是否需要执行迁移动作;
- 动作失败是否回滚。
状态模式也能写这些逻辑,但如果迁移关系非常多,散落在状态类里会比较难看全貌。
状态机通常可以通过配置、表格或框架集中表达这些规则。
五、状态模式可以实现状态机的一部分
两者不是对立的。
一种实现方式是:
- 用状态机配置管理状态和事件;
- 用状态模式封装每个状态下的行为;
- 用状态机引擎统一校验迁移。
也就是说,状态模式偏代码组织,状态机偏规则建模和执行框架。
六、什么时候选哪一个
如果状态数量少,迁移关系简单,状态模式就够了。
比如播放器:
播放中
暂停中
停止
如果状态数量多、事件多、迁移复杂,还需要可视化、审计和配置化,状态机更合适。
比如交易系统、审批流、工单系统,通常不建议只靠简单状态模式硬扛。
七、代码组织模式与规则模型的层次
4 个状态、10 条迁移可用状态类实现;若扩展到 30 个状态、80 条边和并行子状态,显式状态机或工作流更可管理。
state machine model(events/guards/transitions) -> implementation may use State objects/table/engine
状态题的正确性落在“当前状态 + 事件 + 守卫条件 → 目标状态”这一条边上,而不是类数量上。每条边都要说明持久化原子性、重复事件结果和副作用触发时机,非法边则必须得到稳定且可观测的拒绝。
八、迁移一致性、幂等与并发验证
| 检查维度 | 应确认的内容 |
|---|---|
| 机制正确性 | 复杂状态机还需层次状态、超时、持久化和可视化,单纯堆状态类会让边分散。 |
| 适用边界 | 状态模式是一种面向对象组织手段,状态机是描述状态、事件和迁移的计算模型,后者不限定实现形式。 |
| 测试证据 | 用状态×事件矩阵覆盖全部合法边与非法边,并验证两类结果都符合统一错误契约 |
| 工程代价 | 重点评估状态数、迁移边数、一次迁移的数据库竞争,以及新增状态对完整迁移矩阵的影响 |
易错点:复杂状态机还需层次状态、超时、持久化和可视化,单纯堆状态类会让边分散。
九、常见误区与追问
- 误区:用了 State 类就已经完整实现状态机。 若没有事件、合法迁移和守卫条件模型,只是代码拆分类,不能等同完整状态机。
- 误区:把状态码换成状态类就自动消除了所有条件判断。 状态对象只能封装状态相关行为;持久化、并发控制、权限和跨聚合规则仍需显式设计。
- 追问:什么时候用迁移表优于状态类? 状态多、边多且需要集中审计或配置时,表驱动能更直观看到完整迁移矩阵。
- 追问:非法状态迁移应该静默忽略吗? 不应该;应返回明确业务错误并记录当前状态、事件和聚合标识,重复事件则按幂等规则单独处理。
- 追问:状态模式最容易漏掉哪类测试? 非法迁移、重复事件和并发竞争;它们比单线程成功路径更能证明状态模型可靠。
十、加强记忆
状态模式是设计模式,重点是把不同状态下的行为放到不同状态类;状态机是状态流转模型,重点是状态、事件、迁移、动作和守卫条件。简单状态行为用状态模式很清爽,复杂状态流转更适合显式状态机。