在C++中设计状态机

7

我有一个涉及建模状态机的小问题。

我已经进行了一些知识工程和“逆向工程”,确定了一组原始确定性规则,这些规则确定状态以及状态转换。

我想知道关于以下方面的最佳实践:

  • 如何严格测试我的状态和状态转换,以确保系统不会进入未确定的状态。

  • 如何强制执行状态转换要求(例如,应该不可能直接从stateFoo转换到StateFooBar,即为每个状态赋予关于它可以转换到的状态的“知识”)。

理想情况下,我想使用清晰、基于模式的设计,并尽可能使用模板。

虽然我需要一个起点,但我会感激任何指针(不是特意的双关语),帮助我解决问题。


搜索“正式状态机验证”。 - bobah
7个回答

7

也可以看一下MSM(http://www.boost.org/doc/libs/1_55_0/libs/msm/doc/HTML/index.html),它们非常不同。 - bobah

3

哇,看起来并不像它听起来的那么复杂。状态机代码非常简单和短小。

将状态存储在变量中,比如说myState。

你的状态机将是一个switch语句,根据myState变量的值进行分支,以便对每个状态执行代码。

代码将充满类似这样的行:

myState = newState;

为了强制执行状态转换要求,您需要添加一个称为 "instead" 的小方法,就像这样:
void DoSafeStateTransition( int newState )
{
// check myState -. newState is not forbidden
// lots of ways to do this
// perhaps nested switch statement

switch( myState ) {

 …

case X:  switch( newState ) 
    case A: case B:  case Z: HorribleError( newState );
    break;

 ...

}

// check that newState is not undetermined

switch( newState ) {

// all the determined states
case A: case B: case Ccase Z: myState = newState; break;
default: HorribleError( newState );
}
}
void HorribleError( int newState )
{  printf("Attempt to go from %d to %d - disallowed\n",
       myState, newState );
   exit(1);
}

我建议只需要检查代码就能比单元测试做得更好,而且速度肯定更快!

在我看来,单元测试的重点是测试代码必须比被测试的代码简单,这样可以更容易地检查其正确性,然后用于测试复杂的代码。通常情况下,检查状态机代码比检查状态机测试代码要容易得多。如果你不知道单元测试是否正确,那么报告100%的单元测试通过也没有太大意义。

换句话说:编写状态机很容易,设计正确的状态机很难。单元测试只会告诉你是否正确编码了设计,而不会告诉你设计是否正确。


1

测试与模式、模板等几乎没有关系。我建议使用像 CppUnit(xUnit 家族的一部分)这样的测试框架来捕获所有测试用例。当然,具体数量取决于状态机的复杂程度。

你关于强制状态转换的问题涉及到了你的状态机类设计的核心。我认为一个状态将有一组子状态,可以转换到其中的每一个状态,并伴随着触发每个状态转换的事件。如果事件 Foo 没有 FooBar 子状态,则无法转换到它。

我会谷歌“面向对象有限状态机”以开始获取一些设计思路。

当我考虑这样的问题时,我认为组合设计模式可能是其中的一部分,因为状态可能代表更复杂的 FSM。我会有一个状态接口,SimpleState 和 CompositeState 作为实现。我需要重新开始并看看是否能够全部解决。


1
使用状态机偶尔会出现。我通常会像ravenspoint建议的那样,简单地使用switch语句。但是,只有状态不太大才有效。这听起来有点像你的情况。考虑到这一点,我认为最好的方法是从一个良好的架构开始,可以允许你做一些想做的事情。我采纳了duffymo的建议并尝试了Google。这篇论文看起来很有趣-Object-Oriented State Machines。这可能有点过度,但我认为它将提供一个框架,可以轻松测试类似于CppUnit的东西。
谷歌搜索中的其他一些良好参考资料 A Finite State Machine Framework Object-Oriented Finite State Machines

0

听起来像是一个适合单元测试的完美应用程序。市面上有许多单元测试框架,我个人比较喜欢Boost


0

如果你正在寻找经典的GOF设计模式状态机模式,那么请查看wikipedia

在这个页面上(截至撰写本文时),请查看Java示例。

它有一个StateContext类,从示例用法中可以看出,客户端知道writeName方法。实现是:this.myState.writeName(this, name);,这意味着它将调用转发到当前状态,并将自身作为第一个参数传递。

现在看看interface State,它有一个与上述用法匹配的writeName方法。如果您查看StateAStateB,它们会回调上下文设置新状态。

这就是大部分的状态模式。需要意识到的唯一一件事是,StateContext类可以持有其操作涉及的所有数据,包括对当前状态的引用(在C++中必须是指针)。所有状态共同持有所有行为,但没有数据,而是将数据(以及辅助方法)推迟到上下文中。

当我开发状态机时(通常使用TDD),我不会费心测试状态转换,只需确保最终行为符合我的要求。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接