4. 状态机设计模式:分层状态机、嵌套状态机、历史状态模式在审批流中的应用
在复杂的企业审批流中,单一平面状态机往往难以应对多级审批、子流程回退、以及中断恢复等场景。本章深入剖析三种高级状态机设计模式——分层状态机、嵌套状态机与历史状态模式,并结合审批流中的典型错误处理场景,阐述其设计原理与实现要点。
4.1 分层状态机:职责分离与错误隔离
分层状态机(Hierarchical State Machine, HSM)通过将状态划分为多个层级,实现关注点分离。在审批流中,顶层状态描述单据的生命周期(如“草稿”、“审批中”、“已完成”),而子层状态则描述审批过程中的具体阶段(如“部门审批”、“财务审批”、“总经理审批”)。
核心优势:
- 错误隔离:子层状态内的异常(如审批人超时未处理)不会影响顶层状态机的稳定性。
- 复用性:通用的“审批中”顶层状态可被多个子层状态机共享。
- 可读性:状态转换图更清晰,避免状态爆炸。
设计要点: 子层状态机在进入时,需向父层状态机注册其当前状态;父层状态机仅在子层状态机报告“最终完成”或“异常终止”时,才进行顶层状态迁移。
4.2 嵌套状态机:子流程的独立生命周期
嵌套状态机(Nested State Machine)与分层状态机类似,但更强调子状态机的独立性。在审批流中,一个“会签”环节本身就是一个嵌套状态机:它内部包含多个“待签收”、“已签收”、“已拒绝”等状态,且拥有独立的超时与错误处理逻辑。
典型应用场景:
- 会签/并签:需要多个审批人独立审批,每个审批人拥有自己的状态机实例。
- 条件分支:根据金额或部门不同,进入不同的嵌套审批子流程。
- 子流程回退:嵌套状态机内部支持“退回修改”状态,不影响主流程状态。
| 特性 | 分层状态机 | 嵌套状态机 |
|---|---|---|
| 状态归属 | 子状态是父状态的一部分 | 子状态机拥有独立的状态空间 |
| 错误处理 | 子层异常可冒泡到父层 | 子状态机内部处理异常,仅通知父层结果 |
| 生命周期 | 子层随父层创建/销毁 | 子状态机可独立创建和销毁 |
| 典型场景 | 多级审批阶段 | 会签、子流程、并行审批 |
实现建议: 嵌套状态机推荐使用独立的上下文(Context)对象存储子状态机实例。主状态机通过事件(Event)驱动子状态机执行,子状态机通过回调(Callback)返回结果。
4.3 历史状态模式:中断恢复与断点续审
历史状态模式(History State Pattern)用于记录状态机进入某个复合状态前的子状态,以便在重新进入时恢复到上次离开的位置。在审批流中,当审批被“驳回”或“撤回”后,再次提交时应当恢复到之前的审批阶段,而非从头开始。
两种历史类型:
- 浅历史(Shallow History):仅记录当前层级的子状态,不记录更深层嵌套状态。
- 深历史(Deep History):递归记录所有嵌套层级的子状态,实现完全恢复。
4.4 三种模式的组合应用与错误处理策略
在实际企业审批流中,这三种模式往往组合使用,形成强大的错误处理骨架:
- 顶层使用分层状态机管理单据生命周期,确保“审批中”状态下的任何子层异常都不会导致单据状态丢失。
- 会签环节使用嵌套状态机,每个审批人拥有独立状态,超时或拒绝仅影响该子状态机实例。
- 驳回场景使用历史状态模式,记录当前审批阶段和已处理节点,重新提交时精准恢复。
常见陷阱: 历史状态模式在嵌套状态机中应用时,需注意深历史可能带来的状态膨胀问题。建议对历史状态设置最大深度限制(如最多记录3层嵌套),超出部分视为“重新开始”。
4.5 代码示例:历史状态模式在审批流中的实现
以下代码展示了一个简化的历史状态模式实现,用于审批流中断恢复:
// 历史状态管理器
public class HistoryStateManager {
private final Map<String, State> historyStore = new ConcurrentHashMap<>();
// 保存当前状态快照
public void saveSnapshot(String processId, State currentState) {
historyStore.put(processId, currentState.deepCopy());
}
// 恢复历史状态
public State restoreState(String processId) {
State state = historyStore.get(processId);
if (state == null) {
return new InitialState(); // 无历史则从头开始
}
return state;
}
// 清除历史(审批最终完成或取消时调用)
public void clearHistory(String processId) {
historyStore.remove(processId);
}
}
// 审批流状态机中使用历史状态
public class ApprovalStateMachine {
private HistoryStateManager historyManager = new HistoryStateManager();
public void onRejected(String processId, State currentState) {
// 驳回时保存历史
historyManager.saveSnapshot(processId, currentState);
transitionTo(new RejectedState());
}
public void onResubmit(String processId) {
// 重新提交时恢复历史
State restoredState = historyManager.restoreState(processId);
transitionTo(restoredState);
}
}
最佳实践: 历史状态数据建议持久化到数据库或Redis,而非仅存于内存。这样即使服务重启,审批流也能从断点处恢复,避免业务数据丢失。
通过合理运用分层状态机、嵌套状态机与历史状态模式,企业审批流系统能够优雅地处理超时、驳回、撤回、并发冲突等复杂错误场景,构建出健壮、可维护的业务骨架。