【问题标题】:Finite State Machine Pattern - The One True Pattern?有限状态机模式 - 一种真正的模式?
【发布时间】:2011-06-28 10:50:02
【问题描述】:

是否可以通过应用状态机模式来改进曾经编写的所有代码?

我正在从事一个项目,该项目包含大量可怕的、错误的、破碎的意大利面条代码。 我复制了Martin Fowler's example State Machine code from this blog 并将整个垃圾堆变成了一系列语句。 从字面上看,只是状态、事件、转换和命令的列表。

我无法相信这种转变。代码现在很干净,并且可以工作。当然,我之前知道状态机,甚至已经实现了它们 但在 Martin Fowler 的示例中,模型/配置的分离是惊人的。

这让我觉得我所做的几乎所有事情都可以在某种程度上从这种方法中受益。我希望在我使用的每种语言中都有这个功能。 也许这甚至应该是语言级别的功能。

有人认为这是错误的吗? 或者任何人有不同模式的类似经历?

【问题讨论】:

  • 我非常喜欢状态机来解决许多嵌入式编程问题。我确实发现在特殊情况下,模式往往会爆炸(状态大幅增加)或分崩离析(人们违反模式),需求更改等出现在画面中,尤其是当更改是由经验不足/能力不足的开发人员进行时。某些情况(例如动态指定的重试次数)需要注意在 FSM 中正确实施。

标签: design-patterns architecture state-machine


【解决方案1】:

我将提供一个对位。 尽管我肯定时常成功地使用它们,尤其是 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 程序员。

【讨论】:

    【解决方案2】:

    意大利面条代码永远不是正确答案。模式擅长清理意大利面条式代码,但经过深思熟虑的设计也能达到同样的效果。

    关于状态机问题:我个人认为它们非常有用。对于我创建的面向公众的应用程序,我重构为使用状态图(我认为它与状态机相同)并注意到您提到的好处。这是一个非常有用的抽象,因为它允许您将处理事件的关注点分离到一个单独的组件中。有了这个,错误就消失了,因为状态图隐含地知道用户可以在哪里做什么,而且由于事件只在正确的地方处理,你不能真正执行无效的操作。此外,使用状态图使我能够简化代码,因为我可以将所有事件处理代码从它所在的位置拉出来,并将它放在它应该在的地方——换句话说,其他组件没有在它们上也有事件处理程序并不复杂。

    一张照片抵得上一千件作品——这是我想出的设计:

    有了这个,任何看到它的人都确切地知道应用程序在高层次上的行为方式。很难打破这一点,因为就像我说的那样,您不能进入无效状态,因为事件处理程序可以精确控制您可以去哪里;如果您可以进入无效状态,则它是一个很容易修复的实现错误。此外,代码清理的好处是巨大的。例如,使用状态图,每当我进入 任何 暂停状态时,我都可以执行诸如停止时钟、在板上放置掩码等操作。它在 中的几行代码一个地方——因为当我进入暂停的子状态时,图表首先通过父状态。如果没有状态图,我将不得不在处理事件的任何地方执行该代码。

    我不知道它是否需要成为语言级别的功能,但是拥有一个良好实现的框架是很好的。

    【讨论】:

    • 感谢两位的出色回答。我想阅读设计模式是一回事,但真正掌握它的用途是另一回事。
    【解决方案3】:

    有限状态机 (FSM) 和更具体的领域特定语言 (DSL) 通过使用专门的语言描述解决方案,可以更轻松地将问题与特定解决方案领域相匹配。

    状态机模式的局限性在于它本身构成一种编程语言,但您必须为其编写自己的执行、测试和调试工具;任何维护者都必须学习的。您已将代码的复杂性转移到复杂的 FSM 配置中。有时,这很有用,但肯定不是普遍的。

    而且由于任何冯诺依曼计算机本身就是一个 FSM,那么任何程序都可以以这种方式重铸。

    【讨论】:

    • 我想我必须阅读福勒的书。我从未将状态机视为 DSL。我看到它是如何将复杂性转移到其他地方的,但对我来说,这是额外的好处。它是从看似不相关的代码(在我的例子中,比如媒体播放器、会计应用程序和社交网站)中提取出来的,是可重用、经过测试的代码的核心。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-26
    • 1970-01-01
    相关资源
    最近更新 更多