状态定义陷阱:状态爆炸、粒度、命名与合并

状态定义,说白了就是给系统画格子。格子画得太粗,业务逻辑糊成一团;格子画得太细,状态图能绕地球三圈。我见过太多项目在状态定义阶段就埋下了大坑,后面改起来简直是噩梦。

这一章咱们就聊聊状态定义里最常见的四个陷阱。嗯,每一个我都踩过。

状态爆炸问题

什么是状态爆炸?就是你的状态数量像癌细胞一样疯长。本来十几个状态能搞定的事,最后搞出上百个。

为什么会这样?我总结下来,最常见的原因有两个:

  • 把组合当成状态:比如订单有“待支付”、“已支付”、“已发货”,有人非要拆出“待支付-未通知”、“待支付-已通知”、“已支付-未发货”……你想想看,这其实是两个维度的组合,不是独立状态。
  • 把临时标记当成状态:比如“正在加载中”、“正在重试第3次”。这些是瞬态,不是业务状态。
避坑指南:我曾经接手过一个支付系统,状态表里躺着 47 个状态。仔细一查,其中 23 个是“待支付-XX”的组合变体。最后我们用“状态 + 属性”的方式,压缩到了 12 个状态。记住:状态是业务节点,不是变量组合。

怎么判断是不是状态爆炸?一个简单的标准:如果两个状态之间没有本质的业务行为差异,它们就不该是两个独立状态。比如“待支付-未通知”和“待支付-已通知”,系统对它们的处理逻辑完全一样,只是通知标记不同。那就用属性,别用状态。

状态粒度控制

粒度问题,说白了就是状态该粗还是该细。太粗了,状态机像个黑箱;太细了,状态图像张蜘蛛网。

我个人习惯用“行为一致性”来判断粒度。什么意思?就是看这个状态下的所有操作是否一致。

粒度 表现 后果
过粗 一个状态包含多种行为模式 条件判断满天飞,状态机形同虚设
过细 每个微小变化都成一个状态 状态数量失控,维护成本飙升
适中 同一状态下行为一致,变化由属性表达 状态清晰,扩展灵活

举个例子。一个工单系统,有人把“待审核-初审”、“待审核-复审”、“待审核-终审”拆成三个状态。但仔细想想,这三个状态下的操作都是“审核”,只是审核层级不同。更好的做法是:一个“待审核”状态,加一个“审核层级”属性。

我的经验:状态粒度控制在 5-15 个之间比较舒服。少于 5 个,说明你很可能把不同阶段的东西揉在一起了;多于 15 个,就要警惕是不是粒度太细了。

状态命名规范

命名这事,看着小,坑起来要命。我见过一个项目,状态名叫“处理中”、“处理中2”、“处理中_新”。你猜哪个是哪个?没人说得清。

我建议遵循几个简单的规则:

  • 动词 + 名词:比如“待支付”、“已发货”、“已完成”。一眼就能看出这个状态在干什么。
  • 避免数字后缀:什么“状态1”、“状态2”,那是给自己挖坑。
  • 统一时态:要么都用“待XX”,要么都用“已XX”,别混着来。
  • 长度适中:中文 2-6 个字最佳,太长不好读,太短容易歧义。

命名对照表:

❌ 糟糕命名 ✅ 好命名 说明
状态A 待审核 明确表达了业务含义
处理中2 二次处理中 数字后缀改为语义化描述
已完成_旧 已归档 用业务术语代替模糊标记
doing 执行中 统一使用中文

命名规范还有个好处:代码可读性。你想想看,新同事看到“待支付”和“状态3”,哪个更容易理解?

状态冗余与合并

冗余状态,就是那些看起来不同、实际上行为一致的状态。我遇到过最夸张的一个案例:一个电商系统里,“已取消”和“已关闭”两个状态,代码里处理逻辑一模一样。问产品经理,他也说不清区别。最后合并成一个“已取消”。

怎么发现冗余状态?几个信号:

  • 相同的行为映射:两个状态对同一事件的响应完全一样。
  • 相同的后续状态:两个状态能到达的下一个状态集合完全一致。
  • 业务上无法区分:问业务方,他们也说不出这两个状态到底有什么不同。

合并状态的时候,要注意一点:不要丢失信息。如果两个状态虽然行为相同,但业务上确实需要区分(比如统计报表要用),那就用属性来区分,而不是用状态。

避坑指南:我曾经在合并状态时,直接把一个状态删了,结果历史数据全乱了。后来学乖了:合并前先做数据迁移,把旧状态映射到新状态,确保历史数据不丢失。记住:状态合并不只是改代码,还要改数据。

下面这张图,是我总结的状态定义核心逻辑,帮你快速判断一个状态该不该存在:

状态定义决策流程 候选状态 行为是否一致? 是 业务是否可区分? 否 合并状态 是 保留状态 否 决策要点 • 行为一致 + 业务不可区分 → 合并 • 行为一致 + 业务可区分 → 用属性区分,保留一个状态 • 行为不一致 → 保留独立状态

最后说一句:状态定义没有银弹。每个系统都有自己的业务特点,但核心原则是一样的——状态服务于业务,别让状态定义绑架了业务。我见过太多人为了追求“完美的状态机”,把简单问题复杂化了。记住,状态机是工具,不是目的。