8

有限状态机通常被认为是 OOP 中的糟糕设计吗?

我经常听到。而且,在我不得不使用它来处理一个非常古老的、未记录的 C++ 片段之后,我倾向于同意。调试很痛苦。

可读性/可维护性问题呢?

4

7 回答 7

20

FSM永远不应该被认为是坏的。它们太有用了,但不习惯它们的人通常会认为它们很累赘。

有很多方法可以用 OOP 来实现。有些比其他的更丑。您的低级人员将使用 switch 语句、跳转表甚至“goto”。

如果您正在寻找一种更简洁的方法,我建议您使用Boost 的状态图库,它专为在 C++ 中实现 UML 状态图而构建。它利用现代模板技术,使内容更具可读性。它的表现也非常好。

于 2010-02-02T00:52:21.327 回答
9

有限状态机是实现某种目的的工具。作为任何工具,它们也可能被滥用。

它们不是最优雅的工具,但它们擅长的工作几乎不可能通过其他方式实现(通常任何其他方法都注定会比机器糟糕一千倍)。

该作业在禁止经典等待状态的条件下运行。

我必须阅读触摸屏。要读取位置,我必须通过 SPI 交换大约 15 个命令。我需要每秒 100 个读数。我必须在每个命令之后等待大约 1 微秒,以便相应的忙标志消失,然后我才能继续。还有许多其他操作必须在同一界面上实现,例如设置对比度、更改模式、打开或关闭背光、读取温度。如果我while(BUSY_BIT);每次等待都执行,我会在短时间内耗尽所有 CPU。如果我这样做sched_yield()usleep(1),我将永远无法达到我想要的读数数量。唯一的方法是有限状态机。

但是也有一些方法可以让有限状态机发挥得更好。将机器隐藏在幕后,并为开发人员提供可以使用的功能。

到目前为止,我的工作经验主要是基于 3 个不同的有限状态机的 2 个系统。

  1. 一个大型门户网站,在每个步骤中,您都从数据库中检索一些数据,并在此基础上准备更多查询。在最后一步中,您使用数据生成 HTML。每个任务——一个网页模块——被实现为一个继承自引擎的 PHP 类。状态保存在类变量中。每个步骤都是一个单独的功能。在一个步骤结束时,存储的查询被优化并通过缓存发送到引擎,并且答案被提供回原来的。
  2. 具有许多子系统的嵌入式设备。使用任务泵。每个模块注册一个处理程序,该处理程序每​​秒从主循环调用多次。处理程序可以将状态保存在静态或类变量中,并带有状态。这种协作式多任务处理比在单独的线程中运行所有的内存占用要小得多,允许通过两次注册任务来手动确定任务的优先级,并使线程以高优先级运行,从而使系统的其余部分黯然失色。
  3. 半口译员。那个触摸屏。函数调用及其等待状态被注册,但每个只调用一次,然后从程序队列中删除。解释器作为taskpump的任务调用,执行有限数量的函数,直到遇到标记为等待状态的函数(或超过要调用的函数数量)。然后它继续,直到等待状态消失。其他任务将作业作为(有时很长)要执行的函数序列排队,然后等待结果。通过这种方式,我可以将需要创建的状态数限制在需要结果的 4 个左右。如果命令是“发送,从不检查结果”,如“设置对比度”,它们根本不需要离散状态。所以实际状态是“等待事件并注册请求的数据”、“等待测量”和“

如果按结构或顺序编写,代码将简单两倍,清晰三倍。除非它不起作用,或者会以糟糕的性能工作。

于 2010-02-02T11:22:12.970 回答
5

不能告诉你他们说什么。

但是 OO 和 FSM 可以攻击不同的问题域。在对象交互的领域中——这需要面向对象的方法。在世界处于一种或另一种状态的领域中——这需要 FSM 设计。

实际上,您可以将这些设计与/在不同的抽象级别混合,这将比仅使用其中一种更清晰。

于 2010-02-02T00:51:09.890 回答
3

我想说有限状态机比解决相同问题的其他方法(例如匹配常规语言的问题)更容易调试。FSM 的优点就在名称中……您可能有一个具有 15 个状态的状态机,因此您可以在一张纸上绘制一个图表来显示所有转换。您可以使用该图来确定系统的有用属性,例如它接受哪些字符串以及它如何进入错误状态。对于更复杂的系统,绘制图表通常很困难或不可能。

甚至那些说“goto 是邪恶的”的人也认为他们是实现状态机的正确方法。(当然,有些人认为 gotos 总是邪恶的......但你不能取悦所有人)。

于 2010-02-02T00:48:38.710 回答
2

如果代码结构正确,FSM 就很容易理解和维护。我在之前的工作中实现了 FSM,这是一个骨架模板:

密克罗尼西亚联邦

于 2013-05-26T05:55:52.817 回答
1

我认为如果代码有据可查,那么使用 OOP 方法实现它是没有问题的。大多数 C/C++ 使用 switch 语句来实现 FSM,如果机器很大,这有时会损害可读性。

最近需要解析一个Regular Language,用OOP的方式实现了一个FSM,代码可读性和可维护性都不错。我的意思是,比使用大型 switch 语句要好得多。

提示,首先,我已经实现了 FSM 包含状态和包含转换的状态。然而,在我的例子中,它被证明是一种更好的方法,让一个类来表示包含一个状态集合和另一个转换的 FSM。我更容易克隆机器(这是我的要求)并具有更小的转换功能。

我希望它有所帮助,卡洛斯。

于 2010-02-02T00:44:06.130 回答
0

状态机可用于表示任何类的行为。如果传入事件的顺序与类行为(组合类)无关,则使用状态模型不会带来任何特殊好处。

但是,如果一个类的行为取决于传入事件的顺序(顺序类),则状态机代表了行为分析和实现的最佳选择。

如果您关心可读性/可维护性,请使用图形表示。属于不同工程领域的类的行为示例在http://www.StateSoft.org -> State Machine Gallery 以图形和可执行的形式呈现。

-雅努斯

于 2011-09-21T20:19:48.483 回答