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 建模。您知道其他可能更适合我的问题的表示吗?

4

5 回答 5

3

这是第三种选择:

  • 将状态机表示为转移矩阵
    • 矩阵列索引代表一个状态
    • 矩阵行索引代表一个symbol(见下文)
    • 矩阵单元表示机器应过渡到的状态。这可能是新状态或相同状态
    • 每个状态都有OnEvent返回一个方法symbol

FromStateMachine::OnEvent(...)事件被转发到State::OnEvent返回 a symbol- 执行结果。StateMachine然后根据当前状态和返回符号决定是否

  • 必须转换到不同的状态,或者
  • 当前状态被保留
  • 可选地,如果进行了转换,OnExitState并且OnEnterState调用了相应的状态

3 个状态和 3 个符号的示例矩阵

0 1 2
1 2 0
2 0 1

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

第二行说,如果当前状态是0并且返回的符号是1转换状态1。对于 state 1-> state2和对于 state 2-> state 0

类似的第三行表示对于符号2,状态0->状态2,状态1->状态0,状态2->状态1

重点是:

  1. 的数量symbols可能会远低于各州的数量。
  2. 国家彼此不了解
  3. 所有转换都是从一个点控制的,因此当您想要以DB_ERROR不同方式处理符号时,NETWORK_ERROR只需更改转换表并且不要触及状态实现。
于 2012-12-07T13:59:33.297 回答
1

我不知道这是否是您所期望的那种答案,但我习惯以直接的方式处理此类状态机。

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

轻巧灵活。保持代码规则使其具有可读性和“正式性”。

于 2012-12-07T13:38:33.930 回答
1

我个人更喜欢你说的第一种方法。我发现第二个非常违反直觉且过于复杂。为每个状态设置一个类非常简单,如果您在 OnEnterState 中设置正确的事件处理程序并在 OnExitState 中删除它们,您的代码将是干净的,并且所有内容都将自我包含在相应的状态中,以便于阅读。

您还将避免使用大量 switch 语句来选择要调用的正确事件处理程序或过程,因为状态所做的一切在状态本身内部都是完全可见的,从而使状态机代码简短而简单。

最后但同样重要的是,这种编码方式是从状态机绘制到您将使​​用的任何语言的精确翻译。

于 2012-12-07T13:46:08.903 回答
0

对于这种代码,我更喜欢一种非常简单的方法。

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

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

缺点是您不能添加本地化到一个单一状态的其他数据成员。除非您有大量的州,否则这不是一个真正的问题。

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

于 2012-12-07T14:20:37.857 回答
0

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

这个库可能有助于实现一个状态机来控制你的 GUI:PTN Engine

于 2017-10-01T22:06:58.593 回答