TL;博士:
状态机框架应该在哪里/如何确定下一个状态应该是什么?或者,这实际上是否超出了状态机的范围,状态机实际上仅用于跟踪当前状态并验证是否允许请求的转换?
背景和细节:
考虑一个简单的杂志文章发布工作流程。下图显示了对该过程的基本概念理解,我们希望将其转换为代码(在本例中使用Stateless)。它涵盖了出版的基本“快乐之路”,以及一些可能的问题:
┌───┬──────────────────────────────────────────┐
│ W │ ┌───────────┐ │
│ r │ │ │ │
│ i │ │ Write ◄────────┐ │
│ t │ │ │ │ │
│ e │ └───────────┘ │ │
│ r │ │ │ │
├───┼────────Submit───────────│────────────────┤
│ │ │ │ │
│ │ ┌─────▼─────┐ │ │
│ │ │ │ │ │
│ │ │ Review ◄──────┬─│──────────┐ │
│ │ │ │ │ │ │ │
│ │ └───────────┘ │ │ │ │
│ │ │ │ │ │ │
│ │ ____▼___ │ │ │ │
│ E │ / \ │ │ │ │
│ d │ / Grammar \───No──│─┘ │ │
│ i │ \ OK? / │ │ │
│ t │ \________/ │ Resolve │
│ o │ │ Date │ │
│ r │ Yes Passed │ │
│ │ │ │ │ │
│ │ ____▼___ │ │ │
│ │ / \ │ │ │
│ │ / Libel \────Yes│────────┐ │ │
│ │ \ Risk? / │ │ │ │
│ │ \________/ │ │ │ │
│ │ │ ┌───│───┐ │ │ │
│ │ No │ Defer │ │ │ │
│ │ │ └───▲───┘ │ │ │
│ │ ____▼___ │ │ │ │
│ │ / \ │ │ │ │
│ │ / Embargo? \─Yes───┘ │ │ │
│ │ \ / │ │ │
│ │ \________/ │ │ │
│ │ │ │ │ │
├───┼──────────No────────────────────│───│─────┤
│ L │ │ │ │ │
│ e │ │ ┌─────▼───│─┐ │
│ g │ │ │ Legal │ │
│ a │ │ │ Review │ │
│ l │ │ └───────────┘ │
├───┼──────────│───────────────────────────────┤
│ P │ │ │
│ r │ ┌─────▼───────┐ │
│ i │ │ │ │
│ n │ │ Print it! │ │
│ t │ │ │ │
│ e │ └─────────────┘ │
│ r │ │
└───┴──────────────────────────────────────────┘
我的问题是关于如何考虑状态转换,特别是编辑完成审查后的转换。
一种方法(A)是让用户有责任选择适当的过渡;所以在这种情况下,编辑器将有单独的按钮,称为Revert to Check Spelling
,Refer for Legal Review
和Defer Publication
。这些将被连接到对象上的相应方法,根据推荐Article
的方法,这些方法在内部调用相应的方法。.Fire(...)
_stateMachine
Triggers
然而,这至少有两个缺点。首先,它增加了用户的认知负担(在这种情况下,只是勉强,但为了这个例子,请留在我身边)。她不应该选择做什么,她应该能够一次性完成所有的数据输入,表格如下:
┌─────────────────┬──────────────────────┐
│ Spelling OK? │ Yes [ ] No [ ] │
│ Libel Risk? │ Yes [ ] No [ ] │
│ Embargo? │ Date [ dd/mm/yyyy ] │
└─────────────────┴──────────────────────┘
然后应用程序应该决定做什么。这也将防止用户做出错误的选择:回答有关内容的事实问题比在给定潜在的许多输入(想象一个更复杂的示例)的情况下决定正确的行动方案更容易。
第二个缺点是审查可能会过早中断:每当发现一个问题并触发相应的操作时,编辑就无法继续审查以潜在地发现其他问题。一旦第一个问题得到解决并且文章再次返回审查,实际上它又从头开始。
我见过的另一个建议(B)Grammar Issue
是对更多状态进行建模:一个状态,一个Contains Libel
状态等等。然而,这又回到了同样的问题:用户必须单独触发到这些状态的转换(假设并非所有这些问题都可以通过例如语法检查器等自动确定)。
此外,这感觉就像远离我们相对干净的世界模型,或者至少远离状态机库提供的假设清洁度。想象在一个更复杂的例子中状态的扩散,用户输入更多的变量。正如他们所说,我期待使用 Stateless 的导出到 DOT 图功能来实现代码作为权威来源和图表作为副产品。但是,如果输出充满了相当不直观的“状态”,这些状态不再对应于通常理解的工作流“阶段”,那么利益相关者沟通的价值就会降低。
这似乎给我留下了(C),给编辑器一个Submit
函数的选项,它只包含一堆if
语句来确定正确的下一个触发器到.Fire()
. 一方面,这感觉像是从状态机框架旨在提供的优势中倒退(这对无状态来说并不轻视——这个问题的目的是确定我是否持有错误)。另一方面,我认识到我仍然从它的结构中获得了与其他状态相关的好处,并且转换更简单。
还有另一个 SO question提供了一个更简单的示例:
让我们以简单的 ATM 为例。如果用户按下“确认”并且 PIN 正确,则转到状态 2。如果用户按下“确认”并且 PIN 不正确,则转到状态 3。
这个问题似乎更多地询问建模/符号而不是实现,但这个例子类似于我的编辑器按下Submit
她完成的表格的例子。