通过应用状态机模式可以改进所有编写的代码吗?
我正在制作一个项目,这是一个可怕的,可怕的,错误的,破碎的意大利面条代码。 我复制了Martin Fowler's example State Machine code from this blog并将整个垃圾堆转换成了一系列语句。 字面上只是一个国家,事件,过渡和命令列表。
我无法相信这种转变。代码现在很干净,而且很有效。当然,之前我已经了解过State Machines,甚至已经实现了它们 但在Martin Fowler的例子中,模型/配置的分离是惊人的。
这让我觉得我所做过的几乎所有事情都可以从某种方式中受益。我希望在我使用的每种语言中使用此功能。 也许这应该是语言级功能。
有人认为这是错的吗? 或者任何人都有不同模式的类似经历?
答案 0 :(得分:9)
有限状态机(FSM),更具体地说是域特定语言(DSL),通过使用专门语言描述解决方案,可以更轻松地将问题与特定解决方案域相匹配。
状态机模式的局限性在于它本身构成了一种编程语言,但您必须编写自己的执行,测试和调试工具;以及任何维护者必须学习的东西。您已将代码的复杂性转移到复杂的FSM配置中。偶尔,这很有用,但肯定不是普遍的。
既然任何冯·诺依曼计算机本身都是FSM,那么肯定任何程序都可以以这种方式重铸。
答案 1 :(得分:8)
Spaghetti代码永远不是正确的答案。模式很擅长清理意大利面条代码,但只要经过深思熟虑的设计就能达到同样的效果。
关于状态机问题:我个人认为它们非常有用。对于我创建的面向公众的应用程序,我重构使用状态图(我认为与状态机相同)并注意到您提到的好处。它是一个非常有用的抽象,因为它允许您将处理事件的关注分离到一个单独的组件中。有了这个,bug就会消失,因为状态图隐含地知道用户可以在哪里做什么,并且因为事件只在正确的地方处理,所以你不能真正执行无效的操作。此外,使用状态图允许我简化代码,因为我可以将所有事件处理代码从它所在的地方拉出来并将它放在它应该在的一个地方 - 换句话说,其他组件不是因为它们上面还有事件处理程序而变得复杂。
一张图片胜过千件作品 - 这是我想出的设计:
有了这个,任何看过它的人都知道完全应用程序在高级别的行为。很难打破这一点,因为就像我说的那样,你无法进入无效状态,因为事件处理程序可以准确地控制你可以去的地方;如果你可以进入无效状态,它的实现错误很容易修复。此外,代码清理的好处是巨大的。例如,使用状态图表,每当我进入任何暂停状态时,我都可以执行诸如停止时钟,在板上放置掩码等操作。它在中的几行代码一个地方 - 因为当我输入暂停的子状态时,图表首先通过父状态。如果没有状态图,我将不得不在处理事件的任何地方执行该代码。
我不知道它是否需要是一个语言级别的功能,但拥有一个良好实现的框架是很好的。
答案 2 :(得分:0)
我会提供一个对位点。 尽管我确实不时成功地使用过它们,尤其是在 UI 流程中,但这些都是一两个人的项目。我个人认为它们可能是一种反模式。我一直看到这类代码,包括网络协议和机器人 AI:
switch( myState ) {
case Starting:
...
if( such-and-such )
myState = WaitingForHandshake;
break;
case WaitingForHandshake:
...
if( such-and-such )
myState = SauteeingEggs;
break;
case SauteeingEggs:
...
etc
这段代码通常容易出错,需要大量的日志记录和检测。 很难对这样的程序进行推理并让我满意地证明它永远不会处于执行错误代码并检查错误内容的糟糕状态。添加外部调用者更改 myState 以响应事件和更多线程,这是一场灾难。
最近我一直在想,大多数 FSM 像这样可以......展开,因为缺乏更好的词......并被认为是事件的历史加上一个逻辑单元,该逻辑单元对该历史进行查询和然后执行适当的响应。这将需要更多的内存,可能会更慢,但如果仅通过阅读程序是正确的,我就会有更好的想法。我不必绘制状态图或在调试器中逐步完成它,只是为了弄清楚它应该首先做什么。我已经制作了一些相当简单的 AI 机器人,它们避开了经典的 FSM 模式,而是这样做了,但非常成功……但到目前为止,我已经采取了这种思路。
旁注,我可能是另一个时间线中的 Haskell 程序员。