有限状态机模式 - 唯一真实的模式?
是否可以通过应用状态机模式来改进所有编写的代码?
我当时正在做一个项目,里面有大量可怕的、有缺陷的、破碎的意大利面条式代码。 我从这个博客复制了 Martin Fowler 的示例状态机代码并转换了整个一堆废话变成了一系列的陈述。 从字面上看只是状态、事件、转换和命令的列表。
我简直不敢相信这种转变。代码现在很干净并且可以工作。当然,我以前就知道状态机,甚至已经实现过它们 但在 Martin Fowler 的例子中,模型/配置的分离是惊人的。
这让我觉得我所做的几乎所有事情都可以从这种方法中以某种方式受益。我希望我使用的每种语言都有这个功能。 也许这甚至应该是一个语言级别的功能。
有人认为这是错误的吗? 或者有人有不同模式的类似经历?
Could all Code ever written be improved by applying the State Machine Pattern?
I was working on a project that was a mass of horrendous awful, buggy, broken spaghetti code.
I copied Martin Fowler's example State Machine code from this blog and transformed the whole heap of crap into a series of statements.
Literally just a list of States, Events, Transitions and Commands.
I can't believe the transformation. The code is now clean, and works. Of course i was aware of State Machines before and have even implemented them
but in the Martin Fowler example the separation of model/configuration is amazing.
This makes me think that almost everything i've ever done could have benefitted in some way from this approach. I want this functionality in every language i use.
Maybe this should even be a language level feature.
Anyone think this is wrong?
Or anyone have a similar experience with a different pattern?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
有限状态机 (FSM) 和更具体的领域特定语言 (DSL) 通过用专门的语言描述解决方案,可以更轻松地将问题与特定的解决方案领域相匹配。
状态机模式的局限性在于它本身构成了一种编程语言,但您必须为其编写自己的执行、测试和调试工具;也是任何维护人员都必须学习的一门知识。您已将代码的复杂性转移到复杂的 FSM 配置中。有时,这很有用,但肯定不是普遍的。
由于任何冯·诺依曼计算机本身就是一个有限状态机,那么任何程序当然都可以以这种方式重新编写。
Finite state machines (FSM's) and more specifically domain specific languages (DSL's) make it easier to match a problem to one specific solution domain, by describing the solution in a specialised language.
The limitations of the State Machine pattern is that it itself constitutes a programming language, but one for which you have to write your own execution, testing and debugging tools; and one which any maintainer has to learn. You have moved the complexity of your code into a complex FSM configuration. Occasionally, this is useful, but certainly not universally.
And since any von Neumann computer is itself a FSM, then certainly any program can be recast in this fashion.
意大利面条式代码永远不是正确的答案。模式擅长清理意大利面条式代码,但只要经过深思熟虑的设计就可以实现同样的目标。
关于状态机问题:我个人认为它们非常有用。对于我创建的面向公众的应用程序,我重构为使用状态图(我认为这与状态机相同)并注意到您提到的好处。这是一个非常有用的抽象,因为它允许您将处理事件的关注点分离到一个单独的组件中。这样,错误就消失了,因为状态图隐式地知道用户可以在哪里做什么,并且由于事件仅在正确的位置处理,因此您无法真正执行无效的操作。另外,使用状态图使我能够简化代码,因为我可以将所有事件处理代码从原来的位置中取出,并将其放在应该在的地方——换句话说,其他组件不存在。上面还有事件处理程序,这并不复杂。
一幅画抵一千幅作品——这是我想出的设计:
有了这个,任何查看它的人都确切知道应用程序的行为方式一个高水平。很难打破这个,因为就像我说的,你不能进入无效状态,因为事件处理程序准确地控制着你可以去的地方;如果您可以进入无效状态,那么它是一个很容易修复的实现错误。此外,代码清理的好处是巨大的。例如,使用状态图,每当我进入任何暂停状态时,我都可以做一些事情,比如停止时钟、在板上放置一个掩模等。它是中的几行代码一个地方——因为当我进入暂停的子状态时,图表首先通过父状态。如果没有状态图,我就必须在处理事件的每个地方都执行该代码。
我不知道它是否需要成为语言级别的功能,但是拥有一个实现良好的框架是很好的。
Spaghetti code is never the right answer. Patterns are good at cleaning up spaghetti code, but just having a well thought out design can achieve the same thing.
With respect to the state-machine question: I personally find them to be very useful. For a public facing app I created, I refactored to use a state-chart (which I think is the same thing as the state-machine) and noticed the benefits you mentioned. It is a really useful abstraction, because it allows you to separate the concern of handling events out into a separate component. With this, bugs go away because the state-chart implicitly knows what a user can do where, and since the events are only handled in the right place, you can't really perform an invalid action. Also, using the state-chart allowed me to simplify the code because I could pull all the event handling code out of where it was and put it where it in one place where it was supposed to be -- in other words, other components weren't complicated by also having event handlers on them.
A picture is worth a thousand works -- Here is the design I came up with:
With this, anyone who looks at it knows exactly how the app behaves at a high level. Its hard to break this, because like I said, you can't get to an invalid state because the event handlers control exactly where you can go; if you can get to an invalid state, its an implementation bug that is easily fixed. Also, the code cleansing benefits are enormous. For example, with the state chart, whenever I enter any paused state, I can do things like stop the clock, put a mask on the board, etc. Its a few lines of code in one place -- because when i enter a substate of paused the chart goes thru the parent state first. without the statechart, I would have to do that code everywhere an event was handled.
I don't know if it needs to be a language level feature, but having a well implemented framework is nice.
我来提供一个反驳。
尽管我确实时常成功地使用它们,尤其是 UI 流程,但这些都是一两个人的项目。我个人认为它们可能是一种反模式。我经常看到这种代码,比如网络协议和机器人人工智能:
这种代码通常很容易出现错误,并且需要大量的日志记录和检测。
很难推理这样的程序并让我满意地证明它永远不会处于执行错误代码并检查错误内容的糟糕状态。添加外部调用者更改 myState 以响应事件和更多线程,这是一场灾难。
最近我一直在想,像这样的大多数 FSM 都可以......展开,因为缺乏更好的词......并被认为是事件的历史加上一个逻辑单元,该逻辑单元对该历史进行查询,然后执行适当的回应。它会占用更多的内存,可能会更慢,但只要阅读程序,我就会更好地了解程序是否正确。我不必绘制状态图或在调试器中单步调试它来弄清楚它首先应该做什么。我制作了一些相当简单的人工智能机器人,它们避开了经典的 FSM 模式,而是非常成功地做到了这一点……但这就是到目前为止我所采取的这种思路。
旁注,我可能是另一个时间线中的 Haskell 程序员。
I'll provide a counterpoint.
Although I've definitely used them successfully from time to time, particularly with UI flows, those were one-or-two person projects. I personally think they could be an antipattern. I see this sort of code all the time, with things like network protocols and bot AI:
This code is usually prone to bugs and requires lots of logging and instrumentation.
It's hard to reason about programs like this and prove to my satisfaction that it'll never end up in a bad state where it's executing the wrong code and checking for the wrong things. Add external callers changing myState to respond to events and more threads and it's a disaster.
Lately I've been thinking that most FSM's like this could be ... unwrapped, for lack of a better word ... and thought of as a history of events coupled with a logic unit that does queries on that history and then performs the appropriate response. It would take more memory, probably be slower, but I'd have a much better idea if the program was correct just by reading it. I wouldn't have to draw a state diagram or step through it in the debugger just to figure out what it's supposed to do in the first place. I've made some fairly simple AI bots that eschew the classic FSM pattern and do this instead that were quite succesful...but that's as far as I've taken this line of thinking so far.
Side note, I'm probably a Haskell programmer in another timeline.