2012-12-07 131 views
3

我想实现GUI作为状态机。我认为这样做有一些好处和缺点,但这不是这个问题的主题。状态机表示

在阅读了一些这方面的内容之后,我发现了几种使用C++建模状态机的方法,但我坚持使用2,但我不知道哪种方法更适合GUI建模。

  1. 代表的国家机器的状态与以下方法列表:

    • OnEvent(...);
    • OnEnterState(...);
    • OnExitState(...);

    StateMachine::OnEvent(...)我的事件转发到CurrentState::OnEvent(...)和她的做出是否做出过渡的决定。在过渡我打电话CurrentState::OnExitState(...)NewState::OnEnterState()CurrentState = NewState;

    这种方法的国家将紧密结合的行动,但State可能会变得复杂,当从一个状态我可以去多个国家,我必须要为不同的转换不同的动作。

  2. 代表的国家机器作为过渡的列表与以下属性:

    • InitialState
    • FinalState
    • OnEvent(...)
    • DoTransition(...)

    StateMachine::OnEvent(...)我转发事件到InitialState在状态机中具有与CurrentState相同的值的所有转换。如果满足转换条件,则停止循环,调用DoTransition方法,并将CurrentState设置为Transition::FinalState

    使用这种方法Transition将非常简单,但转换计数可能会变得非常高。另外,要追踪一个国家接收事件时将采取的行动将变得更加困难。

您认为哪种方法更适合GUI建模。你知道其他表述可能对我的问题更好吗?

+1

你有没有使用状态机库认为像[Boost.Statechart](http://www.boost.org/doc /libs/release/libs/statechart/doc/index.html)或[Boost.Meta状态机](http://www.boost.org/doc/libs/release/libs/msm/doc/HTML/index。 HTML)? –

+0

我假设你的意思是'current_state-> OnEvent()',而不是'CurrentState :: OnEvent()'? –

+0

@BjörnPollex:这些库提供了什么功能,使得在调试时有必要通过Boost代码? –

回答

3

这里是第三个选项:

  • 代表所述状态机作为过渡矩阵
    • 矩阵列索引表示状态
    • 矩阵行索引表示symbol(见下文)
    • 矩阵单元表示状态machihe应该转移到。这可能是两个新的国家或同一国家
    • 每个州都有OnEvent方法,它返回一个symbol

StateMachine::OnEvent(...)事件转发到State::OnEvent返回一个symbol - 执行的结果。 StateMachine然后,基于当前的状态和返回符号决定

  • 转换到不同状态是否必须进行,或者
  • 当前状态保存
  • 可选地,如果转换时,OnExitStateOnEnterState被称为用于corresponsing状态

实施例基质3个状态和3个符号

0 1 2 
1 2 0 
2 0 1 

在这个例子中,如果如果机器处于任何OD状态(0,1,2)State::OnEvent返回符号0(第一行中的矩阵) - 它保持在相同的状态

第二行表示的是,如果当前状态是0和返回符号1过渡到状态1制造。对于状态1→状态2和状态2→状态0

与之相似第三行表示,对符号2,状态0 - >状态2,状态1 - >状态0,状态2 - >状态1

这之中的一点:

  1. 号码的symbols可能会比国家低得多。
  2. 美国不知道对方
  3. 所有过渡从一个点进行控制的,所以你要处理符号DB_ERROR不同,以NETWORK_ERROR你只需要改变的转换表,不碰状态实现的时刻。
1

我不知道这是你期望的答案,但我用它直接处理这些状态机。

使用枚举类型的状态变量(可能的状态)。在GUI的每个事件处理程序中,测试状态值,例如使用switch语句。做任何需要相应处理的事情,并设置下一个状态值。

轻巧灵活。保持代码的正常性使其可读性和“正式”。

+0

这是第一个版本的一种可能实现 - 对于您评估输入并执行操作的一个状态。我想避免巨大的“评估输入/采取行动”功能,当从一种状态我可以根据输入进入许多状态。 – Felics

+0

@Felics:更常见的结果是“对于一个输入,我可以根据当前状态进入许多状态”。 –

+0

完整的决策表可以包含NI x NS条目(输入次数乘以状态数)。如果你想避免巨大的功能,表格必须是稀疏的,即无数的条目是微不足道的。您可以在输入或状态上压缩表格,具体取决于最佳压缩方式。 –

1

我个人更喜欢你说的第一种方法。我发现第二个是非常直观和过于复杂的。有一类每个状态是简单和容易,如果你再在OnEnterState设置正确的处理事件,并在OnExitState代码中删除他们的将是干净的,一切都将是包含在相应的状态自我,允许很容易看懂。

也将避免巨大的switch语句选择作为一切的状态确实给调用正确的事件处理程序或过程是完全可见的状态里面本身从而使国家机器代码简短。

最后但并非最不重要的,编码的方式是由状态机的精确翻译提请任何语言,你会使用。

+0

这里唯一的问题是难以保留附加的持久数据。你不能将它保存在状态的成员变量中,甚至是继承的成员变量中,因为每个状态必须是不同的对象才能获得虚拟调度。所以你最终需要将帮助器结构传递给每个方法。但是,每个状态数据都很容易,例如计数器。 –

+0

@BenVoigt:Persistend数据属于状态机本身,并且每个状态在构建时都会接收对其所有者状态机的引用。 – BlackBear

+0

的确,您需要通过另一个对象才能获取数据。不是一个展示瓶塞,但肯定会使代码更加冗长。 –

0

我更喜欢这种类型的代码非常简单的方法。

  • 枚举状态。
  • 每个事件处理程序在决定要采取的操作之前检查当前状态。操作只是switch声明或if链中的复合块,并设置下一个状态。
  • 当行为变得超过几行或需要重用时,重构为分离辅助方法的调用。

这种方式没有额外的状态机管理元数据结构,也没有代码来管理元数据。只是你的商业数据和转换逻辑。动作可以直接检查和修改所有成员变量,包括当前状态。

缺点是您无法添加本地化为单一状态的其他数据成员。除非你有非常多的状态,否则这不是一个真正的问题。

如果您始终在进入每个状态时配置所有UI属性,而不是假设以前的设置并创建状态退出行为以在状态转换之前恢复不变量,我发现它还会导致更强大的设计。无论您用于实施转换的方案如何,这都适用。

0

您也可以考虑使用Petri网对期望的行为进行建模。如果你想实现一个更复杂的行为,这将是更可取的,因为它允许你确定所有可能的场景并防止死锁。

这个库可能是有用的实施状态机来控制你的GUI:PTN Engine