状态机与工作流模式:有限状态机、状态转移表、工作流引擎、状态持久化

大家好,我是老赵。今天我们来聊聊状态机和工作流。这两个东西,说白了就是让程序“有规矩地动起来”。

我刚开始写代码那会儿,特别喜欢用一堆 if-else 来控制流程。结果呢?代码越写越乱,改一个地方,炸一片。后来我学乖了——状态机这东西,真香。

有限状态机:程序员的“交通规则”

有限状态机,英文叫 Finite State Machine,简称 FSM。它不是什么高深理论,就是一套规则:当前状态 + 输入事件 → 下一个状态

举个例子,一个简单的订单系统:

  • 状态:待支付、已支付、已发货、已完成、已取消
  • 事件:支付、发货、确认收货、取消

你看,这不就是现实世界的映射吗?

核心三要素:

  • 状态(State):系统当前所处的阶段
  • 事件(Event):触发状态变化的条件
  • 转移(Transition):状态之间的跳转规则

我个人习惯,在设计状态机时,先画一张图。别急着写代码,把状态和转移理清楚,后面就顺了。

状态转移表:把规则写进表格里

状态转移表,就是状态机的“说明书”。它用表格的形式,把每个状态在遇到不同事件时的行为列出来。

我曾经在一个支付网关项目里,用状态转移表来管理交易状态。效果出奇的好——测试同学拿着表就能写用例,开发同学对着表就能写代码。

当前状态 事件 下一个状态 动作
待支付 支付成功 已支付 发送支付通知
待支付 取消 已取消 释放库存
已支付 发货 已发货 更新物流信息
已发货 确认收货 已完成 结算给商家

嗯,这里要注意:状态转移表一定要覆盖所有状态和事件的组合。漏掉一个,线上就可能出 bug。

工作流引擎:让复杂流程自动跑起来

当状态机变得复杂,比如涉及多人审批、超时处理、条件分支,这时候就需要工作流引擎了。

市面上主流的两个:ActivitiCamunda。它们都支持 BPMN 2.0 标准,说白了就是用图形化的方式定义流程。

Activiti:轻量级,适合嵌入

Activiti 比较轻,适合作为库嵌入到你的应用中。我几年前做一个 OA 系统,就用 Activiti 来处理请假审批流程。部署简单,API 也清晰。

// 部署流程定义
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
repositoryService.createDeployment()
    .addClasspathResource("leave.bpmn20.xml")
    .deploy();

// 启动流程实例
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance instance = runtimeService.startProcessInstanceByKey("leaveProcess");

Camunda:功能更全,社区活跃

Camunda 是从 Activiti 分出来的,功能更丰富,社区也更活跃。它自带一个不错的流程设计器,还有 Cockpit 监控界面。

我记得有一次帮客户做供应链系统,流程特别复杂——有并行网关、会签、子流程。用 Camunda 的 BPMN 模型,拖拖拽拽就搞定了,比写代码省事多了。

我的建议:

如果你的流程比较简单(少于10个节点),手写状态机就够了。流程复杂了,再上工作流引擎。别一上来就上重武器。

状态持久化:别让状态“失忆”

状态机跑在内存里,一重启就没了。怎么办?持久化。

常见的做法有几种:

  • 数据库存状态字段:最简单,订单表里加个 status 字段
  • 事件溯源:不存当前状态,存所有事件,状态由事件推导出来
  • 工作流引擎自带持久化:Activiti 和 Camunda 都支持把流程实例持久化到数据库

我曾经踩过一个坑:用数据库存状态,但没考虑并发。两个请求同时修改同一个订单的状态,结果状态乱了。后来加了乐观锁,才解决。

避坑指南:

我曾经在状态持久化时,只存了当前状态,没存历史状态。结果线上出问题,想回溯都找不到线索。后来我改成存状态变更日志,每个状态变化都记录下来。排查问题方便多了。

知识体系总览

下面这张图,是我自己总结的状态机与工作流的知识体系。你把它理清楚了,这块就算入门了。

状态机与工作流 有限状态机 状态 + 事件 → 转移 状态转移表 规则表格化 工作流引擎 Activiti / Camunda 状态持久化 数据库 / 事件溯源 应用场景 订单系统 | 审批流程 | 支付网关 | 工作流自动化 最佳实践 先画图 → 写转移表 → 选合适工具 → 做好持久化 记录状态变更日志 | 处理并发冲突 | 覆盖所有边界情况

总结一下

状态机和工作流,说白了就是让复杂流程变得可控。你想想看,如果没有这些模式,一个订单系统得写多少 if-else?

我个人经验是:小项目用手写状态机,大项目上工作流引擎。别纠结用什么工具,先把状态和转移理清楚。状态转移表写好了,代码自然就出来了。

嗯,今天就聊到这儿。记住:状态机不是银弹,但它能帮你把代码写得更有条理。下次写复杂逻辑时,试试先画个状态图?


公众号:蓝海资料掘金营,微信 deep3321