3、状态定义与迁移:定义IDLE、LOCK、UNLOCK、CHILD_LOCK等状态,绘制状态迁移图

好,咱们进入第三章。这一章要聊的,是状态机里最核心的东西——状态本身怎么定义,以及它们之间怎么跳来跳去。

说实话,我见过不少新手工程师,一上来就急着写代码。结果呢?写到一半发现状态少了,或者迁移条件搞混了,回头再改,那叫一个痛苦。我个人习惯是,先花半小时把状态图画清楚,后面写代码基本就是照着翻译,省心多了。

3.1 状态定义:门控中控锁的四种基本状态

咱们这个门控中控锁系统,说白了就四种核心状态。你想想看,车门锁还能玩出什么花来?

  • IDLE(空闲态):系统上电后的初始状态。啥也不干,等着接收指令。这时候车门锁的状态保持上一次的设定。
  • LOCK(上锁态):所有车门都锁上了。从外面打不开,从里面可以手动解锁。
  • UNLOCK(解锁态):所有车门都解锁了。内外都能开。
  • CHILD_LOCK(儿童锁态):这个比较特殊。后门从里面打不开,只能从外面开。我当年做第一个项目时,就把这个状态给漏了,结果测试时发现后门逻辑不对……嗯,后来老老实实补上了。

重要提醒:CHILD_LOCK 不是 LOCK 的子集。它是独立的状态。有些系统会把儿童锁当成 LOCK 的一个属性,但那样会让状态迁移变得复杂。我建议把它单独拎出来,清晰明了。

3.2 状态迁移:谁可以跳到谁?

状态不是随便跳的。你得想清楚,什么条件下能从 A 到 B。咱们用表格来梳理一下,这样更直观。

当前状态 触发事件 下一状态 说明
IDLE 收到 LOCK 指令 LOCK 用户按下锁车键
IDLE 收到 UNLOCK 指令 UNLOCK 用户按下解锁键
IDLE 收到 CHILD_LOCK_ON 指令 CHILD_LOCK 驾驶员开启儿童锁
LOCK 收到 UNLOCK 指令 UNLOCK 解锁所有门
LOCK 收到 CHILD_LOCK_ON 指令 CHILD_LOCK 注意:从 LOCK 到 CHILD_LOCK 时,车门依然是锁着的,但后门内部打不开
UNLOCK 收到 LOCK 指令 LOCK 上锁
UNLOCK 收到 CHILD_LOCK_ON 指令 CHILD_LOCK 解锁状态下开启儿童锁
CHILD_LOCK 收到 LOCK 指令 LOCK 儿童锁状态下上锁
CHILD_LOCK 收到 UNLOCK 指令 UNLOCK 儿童锁状态下解锁
CHILD_LOCK 收到 CHILD_LOCK_OFF 指令 IDLE 关闭儿童锁,回到空闲态

我的小技巧:画迁移表时,我习惯把「不可能发生的迁移」也列出来。比如从 LOCK 直接跳到 LOCK?没意义。从 UNLOCK 直接跳到 IDLE?也不行。把这些排除掉,能帮你发现设计漏洞。

3.3 状态迁移图:一图胜千言

表格归表格,但真正好用的还是状态图。我一般用 UML 状态图来画,工具嘛,PlantUML、draw.io 都行。下面我用文字描述一下这个图的样子,你可以在脑子里画出来,或者直接照着画。

状态图描述:

- 四个状态节点:IDLE(左上)、LOCK(右上)、UNLOCK(左下)、CHILD_LOCK(右下)
- 箭头和标签:
  - IDLE → LOCK : 收到 LOCK 指令
  - IDLE → UNLOCK : 收到 UNLOCK 指令
  - IDLE → CHILD_LOCK : 收到 CHILD_LOCK_ON 指令
  - LOCK → UNLOCK : 收到 UNLOCK 指令
  - LOCK → CHILD_LOCK : 收到 CHILD_LOCK_ON 指令
  - UNLOCK → LOCK : 收到 LOCK 指令
  - UNLOCK → CHILD_LOCK : 收到 CHILD_LOCK_ON 指令
  - CHILD_LOCK → LOCK : 收到 LOCK 指令
  - CHILD_LOCK → UNLOCK : 收到 UNLOCK 指令
  - CHILD_LOCK → IDLE : 收到 CHILD_LOCK_OFF 指令
- 初始状态:IDLE(用一个小黑点指向它)

你可能会问:「为什么 CHILD_LOCK 能直接回到 IDLE,而 LOCK 和 UNLOCK 不行?」

嗯,这个问题问得好。因为 CHILD_LOCK 是一个「叠加态」,它不影响车门的上锁/解锁状态。关闭儿童锁后,系统应该回到 IDLE,然后根据实际车门锁状态再决定下一步。说白了,IDLE 就是系统的「休息区」,所有特殊状态处理完后,都回到这里。

3.4 避坑指南:我曾经踩过的坑

讲几个我实际项目中遇到的坑,你写代码时注意避开。

  • 坑一:状态遗漏。 我刚开始做时,只定义了 LOCK 和 UNLOCK,结果客户要求加儿童锁,我临时加了个状态,导致迁移逻辑乱成一团。后来我学乖了,一开始就把所有可能的状态列全,哪怕暂时用不到。
  • 坑二:迁移条件冲突。 比如同时收到 LOCK 和 UNLOCK 指令怎么办?我建议在状态机入口做仲裁,或者加个优先级。我一般让 LOCK 优先级高于 UNLOCK,毕竟安全第一。
  • 坑三:状态图太复杂。 有些人喜欢把每个按钮、每个传感器都画成独立迁移,结果图上一堆箭头,自己都看不懂。记住,状态图是给人看的,不是给机器看的。保持简洁,必要时用注释说明。

特别注意:不要试图在状态图里处理所有异常情况。比如车门锁电机卡住了、传感器失效了,这些应该在状态机的「异常处理」模块里处理,而不是塞进状态迁移里。否则你的状态图会变成一团乱麻。

3.5 小结

这一章我们定义了四种核心状态,画出了它们之间的迁移关系。说白了,状态机就是一张地图,告诉你现在在哪,能去哪,怎么去。下一章我们会把这些状态翻译成代码,到时候你就知道,前面画图花的功夫,全是值得的。

记住我一句话:状态图画得越清楚,代码写得越痛快。 别急着敲键盘,先把图搞定。