在 .NET 中使用有限状态机实现工作流建模 - Lloyd


希望这篇文章能帮助你了解复杂和深度嵌套的条件代码的缺点,并为如何编写更容易理解和维护的代码带来新的视角:

  • 解释了复杂且深度嵌套的条件代码的问题,因为它需要匹配域模型而很常见
  • 介绍了状态机的概念,以及它如何成为降低复杂性和使某些状态不可表示从而减少引入错误的能力的解决方案
  • 区分公共 API 和文档的内部实现细节
  • 创建了工作流程图并将其转换为代码,同时强调了业务分析师/领域专家参与其中的必要性
  • 使用集中式错误处理机制处理无效转换
  • 举例说明如何使用 FSM 进行单元测试代码

需求问题
想象一下,一份文件在发送给客户之前需要被 "批准"--检查从语法和拼写错误到商定的服务和价格等任何东西。然后,一旦它被批准,它就会被发送给客户。客户必须批准、拒绝或要求修改。无论他们选择哪种方式,文件都会被送回给审批者作进一步审查。这样一直持续到文件完成,并且每个利益相关者都批准了它。

让我们做一些近似的数学计算,看看这个看似简单的工作流程会有多少个步骤或状态。

  • 在它被批准或发送给客户之前,它显然处于某种状态。我把这称为 "草稿"。
  • 它必须得到内部利益相关者的批准或要求修改。
  • 如果要求修改,那么它就会进入自己的一套状态。
  • 客户批准该文件。
  • 客户拒绝该文件。
  • 法律团队批准该文件。

以此类推...
这已经是6个以上的状态了,但还有很多。状态越多,工作流程就越复杂。如果我们试图用代码来模拟这个工作流程,我们很快就会有几十个甚至几百个标志来代表 "当前 "状态。这还没有考虑到带有额外信息的状态,比如 "更改请求"。"改变标题"。此外,这甚至没有考虑到可以同时设置的标志的数量。
  • 在 "状态 "和 "步骤 "之间没有明确的区分--例如,一些步骤可以导致相同的状态。
  • 一个新的功能可能会对代码产生很大的影响--也可能根本就没有。没有简单的方法可以知道。
  • 每一个新的状态都会增加标志的排列组合的数量
  • 即使有单元测试,代码仍然很复杂,难以维护
  • 随着更多状态的加入,错误也在悄悄出现,重构也变得更加困难。
  • 没有遵循使非法状态无法呈现的概念--无效的状态太容易被意外地变成可能,导致业务逻辑的缺陷。如果我们能尽可能多地转移到类型系统中,我们应该这样做。
  • 所有这些因素很快就会导致代码不再符合领域的情况


有限状态机的解决方案
可能有无数的解决方案,但我要为这个 "文档工作流 "问题提出的解决方案是使用一个有限状态机。首先,对有限状态机进行正式定义。

有限状态机(FSM)或有限状态自动机(FSA,复数:自动机),有限自动机,或简单的状态机,是一种计算的数学模型。它是一种抽象的机器,在任何时候都可以准确地处于有限数量的状态中的一种。FSM可以根据一些输入从一个状态改变到另一个状态;从一个状态到另一个状态的改变被称为转换。一个FSM由其状态列表、初始状态和触发每个过渡的输入来定义。
- 维基百科

FSM是一个 "通用 "概念,实际上可以在任何地方使用,特别是数学、电子工程/嵌入式系统、科学和软件工程。
在这个特定的案例中,我们用它来模拟软件中的 "文件工作流程",以取代不必要的复杂和嵌套的逻辑状态。
还值得指出的是,FSM可以被用来为广泛的领域建模。例如,用户界面也可以用它们来实现,原因与我之前列举的完全一样;管理复杂性,使状态和转换更加明确。

记住,创建FSM的第一步是弄清所需的状态和转换。仅仅这一步就是让业务分析师/领域专家/产品团队/任何人都能参与进来的绝佳机会。这也将迫使我们进行一些前期设计,以确保代码与领域正确匹配。