有限状态机:设计不好?

发布于 2024-08-19 18:33:43 字数 127 浏览 6 评论 0原文

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

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

可读性/可维护性问题又如何?

Are Finite State Machines generally considered as bad design in OOP ?

I hear that a lot. And, after I had to work on a really old, undocumented piece of C++ making use of it, I tend to agree. It was a pain to debug.

what about readability/maintainability concerns?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

旧伤慢歌 2024-08-26 18:33:43

FSM 永远不应该被认为是不好的。它们太有用了,但不习惯它们的人常常会认为它们是负担。

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

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

FSMs should never be considered bad. They are far too useful, but people whom aren't accustomed to them will often consider them burdensome.

There are numerous ways to implement one with OOP. Some are uglier than others. Your low-level guys will use switch statements, jump tables or even "goto."

If you're looking for a cleaner way to do it, I'd recommend Boost's State Chart library, which is built just for implementing UML state diagrams in C++. It makes use of modern template techniques, to make things more readable. It also performs very well.

北城孤痞 2024-08-26 18:33:43

有限状态机是实现特定目标的工具。与任何工具一样,它们也可能被滥用。

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

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

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

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

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

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

如果按结构或顺序编写,代码将简单两倍、清晰三倍。除非它不起作用,或者性能很差。

Finite state machines are a tool to achieve certain end. As any tool, they can be abused too.

They are not the most gracious of tools, but the work they are good at is about impossible to achieve by other means (and usually any other approach is then doomed to be a horrible mess thousand times worse than the machine).

The job is operating in conditions where classic wait states are forbidden.

I have to read touchscreen. To read the position, I have to exchange about 15 commands over SPI. I need good 100 readouts a second. I have to wait about 1 microsecond after each command, for respective busy flag to vanish before I can continue. There is also a number of other operations that must be attainable over the same interface, like setting contrast, changing modes, turning backlight on or off, reading temperature. If I performed while(BUSY_BIT); for each wait, I would eat up all the CPU in matter of moments. If I did sched_yield() or usleep(1), I would never attain the number of readouts I want. The only way is a finite state machine.

But there are ways to make the finite state machine play nice too. Hide the machine behind the scenes and give the developers functions to work with.

My job experience so far was dominated by 2 systems based on 3 different finite state machines.

  1. a big web portal, where in each step you retrieve some data from the database, and basing on it prepare more queries. In the last step you use the data to generate HTML. Each task - a webpage module - was implemented as a PHP class inheriting from the engine. State was preserved in class variables. Each step was a separate function. At end of a step, stored queries were optimized and sent out to the engine, through caches, and the answers were provided back to the original.
  2. an embedded device with many subsystems. Task Pump is used. Each module registers a handler that is called many times a second from the main loop. The handler may preserve state in static or class variables, with states. This cooperative multitasking allows for much smaller memory footprint than running all in separate threads, allows for manual prioritizing of tasks by registering them twice, and has the thread run at high priority, overshadowing the rest of the system.
  3. semi-interpreter. That touchscreen. Function calls and their wait states are registered, but each is called only once, then removed from the program queue. The interpreter is called as a task of taskpump, executing a limited number of functions until encountering a function marked as a wait state (or exceeding the number of functions to be called). Then it continues until the wait state vanishes. Other tasks enqueue jobs as (sometimes long) sequences of functions to be executed, then wait for result. This way I can limit the number of states I need to create to about 4 where I need results. If the command is of "send away, never check result" like "set contrast", they don't need discrete states at all. So the actual states are "wait for event and register requested data", "wait for measurement" and "read results and assign them properly".

The code would be twice as simple and three times clearer if written structurally or sequentially. Except it would't work, or would work with abysmal performance.

蘸点软妹酱 2024-08-26 18:33:43

无法告诉你他们说什么。

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

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

Couldn't tell you what They say.

But OO and FSM sorta attack different problem domains. In a domain where objects are interacting -- that calls for an object-oriented approach. In a domain where the world is in one state or another -- that calls for a FSM design.

Realistically, you can mix these designs with/at different levels of abstraction, which will come out cleaner than using only one or the other.

清风不识月 2024-08-26 18:33:43

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

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

I would say that finite state machines are much easier to debug than other ways of solving the same problems (problems such as matching regular languages). What's nice about FSMs is all in the name... you might have a state machine with 15 states, so you can draw a diagram on a piece of paper showing all the transitions. You can use the diagram to figure out useful properties of the system, such as which strings it accepts and how it gets into error states. With more complicated systems, diagramming is often difficult or impossible.

Even the people who say "gotos are evil" think they're the right way to implement state machines. (Of course, some people think gotos are always evil... but you can't please everybody).

北风几吹夏 2024-08-26 18:33:43

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

FSM

FSM's can be easy to understand and maintain if the code is structured the right way. I've implemented an FSM in prior jobs, here is a skeleton template:

FSM

网白 2024-08-26 18:33:43

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

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

提示,首先,我已经实现了包含状态的 FSM 和包含转换的状态。然而,就我而言,事实证明,使用一个类来表示包含一个状态集合和另一个转换集合的 FSM 是一种更好的方法。它让我更容易克隆机器(这对我来说是一个要求)并且具有更小的转换函数。

我希望它有帮助,
卡洛斯.

I think if the code is well documented there is no problem to implement it using a OOP approach. Most of the C/C++ use to implemente FSM using switch statements which sometimes can compromise the readability if the machine is large.

Recently I needed to parse a Regular Language, and implemented an FSM using the OOP approach, the code readability and maintainability were good. I mean, much better than using large switch statements.

A tip, at a first moment, I've implemented the FSM containg states and the states containing the transitions. However, in my case it proved to be a better approach to have a class to represent the FSM containing one collection of states and another of transitions. It made easier to me to clone the machines (it was a requirement to me) and to have a smaller transition function.

I hope it helps,
Carlos.

撞了怀 2024-08-26 18:33:43

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

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

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

-贾努斯

State Machine can be used to represent behavior of any Class. If the order of incoming events does not matter to a Class behavior (combinatorial Class), use of State Model delivers no special benefit.

However if the behavior of a Class depends on the order of incoming events (sequential Class) State Machine represents the best choice for behavior analysis and implementation.

If readability/maintainability is your concern, use graphical representations. Examples of behavior of Classes belonging to different engineering domains are presented in a graphical and executable form at http://www.StateSoft.org -> State Machine Gallery.

-Janusz

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文