复合状态与历史伪状态
当一个对象的状态机节点过多时,可以用复合状态把子状态机包进一个状态里:外部只看到一个状态,内部自己维持多条子状态。复合状态内可以划出多个区域(region)并行运行;历史伪状态用来记住“上次离开时的子状态”,浅历史 H 恢复一层,深历史 H* 恢复全部嵌套层次。它们的作用是把复杂流程分层描述,而不是把所有组合平铺成一张巨图。
基本信息
| 复合状态 | 状态内部再含状态机,可简化外部转换 |
|---|---|
| 区域(region) | 同一复合状态内并行运行的多条子状态机 |
| 历史伪状态 | 浅历史 H 恢复一层,深历史 H* 恢复全部嵌套层次 |
| 进入语义 | 进入复合状态默认走其初始伪状态,除非显式指向历史伪状态 |
典型使用场景
- 订单与运单流转:运输中这类长阶段内部再分干线、分拨、末端
- 设备连接管理:连接态内部再分注册、鉴权、保活子状态
- 审批流程建模:审批中内部包含多级审批与退回分支
- 游戏或仿真状态:并行区域分别管理角色状态与场景状态
- 中断恢复场景:用历史状态恢复中断前的子状态,避免从头开始
复合状态示例(PlantUML 片段)
复合状态示例(PlantUML 片段)
state 运单流 {
[*] --> 待揽收
待揽收 --> 运输中 : 揽收
state 运输中 {
[*] --> 干线
干线 --> 末端 : 到达分拨
}
运输中 --> 已签收 : 妥投
}历史伪状态对照
| 用法 | 恢复范围 | 适用 |
|---|---|---|
| 浅历史 H | 只恢复到复合状态最近一次的直接子状态 | 一层嵌套即可表达的场景 |
| 深历史 H* | 恢复全部嵌套层次的子状态 | 多层嵌套且需要原样恢复的场景 |
| 无历史 | 重新进入初始子状态 | 每次进入都从头开始的流程 |
使用要点
- 复杂状态机建议分层:先画一级状态,再把每个状态单独展开,避免一张图几百个节点
- 历史状态只在确有恢复语义时使用,滥用会让行为依赖隐式记忆
- 并行区域之间只通过消息或事件交互,不共享可变状态
- 命名用业务词汇而非技术词汇,评审时业务方能直接核对
复杂状态机建议分层:先画一级状态,再把每个状态单独展开,避免一张图几百个节点;分层的图也更便于按子状态机分配实现责任。
深历史恢复会让对象行为依赖隐式记忆:测试要构造“进入-离开-再进入”的序列才能覆盖,遗漏时问题只在特定重入路径上出现。