为什么开发者们从不使用状态机

原文: Why Developers Never Use State Machines,觉得写得挺好,于是试着翻译。

Why Developers Never Use State Machines

几个月前,我在Shopify博客上看到了一篇很棒的关于状态机的小博文。博文想表达的是,状态机很棒,开发人员应该更多地使用他们—— 根据我最近在CrowdHired(译注:作者的网站?)中使用状态机的经历,我很赞同他的观点。但这篇博文让我思考,在我的开发者职业生涯中,有多少次真正用到了状态机(包括使用库或是自己做的抽象)? 答案是0次,这让我非常震惊,比当我知道状态机非常有用时更加的震惊。所以我决定反省一下,找出原因——为什么我们更倾向于以一种临时的方式来管理我们的「state」 「stauts」,却不用我们都推崇的方式。

We Don’t Need One Until We Do

行动之前,我们不会觉得自己需要它

问题是,你几乎永远也无法构建一个能满足所有需求的对象,而是会渐进地添加新特性。所以,一开始会觉得你的对象不会有太复杂的状态,复杂到需要一个「完整的」状态机(YAGNI原则),直到某一天,你才发现它已经变得很复杂——让感到你要投入的时间都已经能把它从头到尾重写一个了,进退两难。这种问题的杀伤力很高,但在事情变糟之前没什么影响,但意识到的时候就已经太晚了。

A State Machine Is A Fluffy Bunny (Not Particularly Threatening)

状态机是一只蓬松的兔子(不怎么危险)

对于我们这些拿到计算机科学学位的人,谈起计算理论课里关于状态机的知识,记忆里总是没好感的——复杂的图表和符号,确定性状态机或是非确定性状态机,Moore 和 Mealy 状态机,还有那些缩写(DFA,NFA,GNFA等)。这使我们高估了状态机的复杂性,实用主义让我们觉得「完备的」状态机实在是大材小用了。

但是,在你的日常开发中用的状态机,和那些在计算机理论里描述的东西,通常没半毛钱关系(除了.. errr.. 它的理论)。用字符串表示一些状态(states),用方法(method)表示那些引发状态转换的事件(event)——几乎都是这样(至少Ruby里的state_machine开发包中是这样)。重点是,即使你只有两个状态,使用状态机也并不是大材小用,这样做更容易使一个临时的方案不断迭代下去,前提是你有好用的状态机库。

Even A Good Tool Is Not A Good Tool

好工具也不一定是好工具

我敢打包票,你能用到的大多数语言中,都有好用的状态机库(上述Ruby中的state_machine只是个例子)。但即使是一只蓬松的兔子,也有一定的学习曲线。当然,一般一开始也不会考虑它的学习曲线,但是,因为我们往往倾向在事后才开始用状态机库(临时解决方案依旧可用的情况下),这时学习曲线就是个问题了。蕴含“潜在的未来收益”的东西,它的直接价值是很难被证明的,就算当下只是需要说服自己(除非你有过经验)。平滑的学习曲线,只能加强你的”我们可以在没有它的条件下活下来”这种想法。一个工具,如果不去使用它,不管它有多好都毫无意义.

懂的人才能懂——如果你给好用的状态机库一个机会,生活会如此美好。当我们终于在 CrowdHired 上“忍辱负重”地对用状态机库,对我们的一些核心模块进行重构,效果显而易见:

  • 首先,学习曲线不算什么,我确实花了几个小时浏览源代码和文档,但在那之后我很清楚什么可以做,什么不能做

  • 添加状态机几乎没有痛苦,但移动与新状态机关联的代码很痛苦。事后看来,如果在对象只有几个状态时就行动,那就简单多了

  • 我们现在能够轻松地引入更多状态,为我们的用户提供额外的信息,并允许我们更精细地跟踪事物。之前因 YAGNI 造成的痛苦,现在我们发现“之前就已经需要了”,因为它很容易做到

  • 现在状态转换相关的返回值现在 100% 一致(只有 True / False),而之前,可能返回的是对象数组、nil、true/false ,取决于谁在何时编写它(译注:大概是在说执行操作返回的结果更清晰了?)

  • 现在我们可以简单地通过加入 temachine-audit_trail 来跟踪我们的状态转换,而之前很难明确在哪做个 hook,所以我们什么都没做

  • 我们删除了一堆代码并改进了我们的代码库——就我而言,这始终是有价值的目标。

大部分读过那篇 Shopify文章 的人,都会从精神上同意它的观点,但不会付出行动(和我一样)。我们似乎是有意回避着状态机,因为他的复杂性,以及无法量化的好处。但是,实际上它并没有你想象的那么复杂,能带来的好处比你预期的更多,只要你不要在之前提到的糟糕情况发生之后才开始改用状态机。所以,下次当你开始创建一个有些“状态”苗头的对象时,只管往那放一个状态机就好,之后会为它高兴的。I guarantee it or your money back :)