我想将GUI实现为状态机。我认为这样做有一些好处和一些缺点,但这不是这个问题的主题。
在对此进行一些阅读后,我发现了几种用C ++建模状态机的方法,我坚持使用2,但我不知道哪种方法更适合GUI建模。
使用以下方法将状态机表示为状态列表:
OnEvent(...);
OnEnterState(...);
OnExitState(...);
从StateMachine::OnEvent(...)
我将事件转发到CurrentState::OnEvent(...)
,此处决定是否进行转换。在转换时,我致电CurrentState::OnExitState(...)
,NewState::OnEnterState()
和CurrentState = NewState;
使用这种方法,状态将与动作紧密耦合,但State
从一个状态我可以进入多个状态时可能变得复杂,我必须针对不同的转换采取不同的动作。
将状态机表示为具有以下属性的转换列表:
InitialState
FinalState
OnEvent(...)
DoTransition(...)
从StateMachine::OnEvent(...)
我将事件转发到状态机中InitialState
与CurrentState
具有相同值的所有转换。如果满足转换条件,则停止循环,调用DoTransition
方法并将CurrentState
设置为Transition::FinalState
。
使用这种方法Transition
将非常简单,但过渡计数可能会非常高。此外,当一个州收到一个事件时,跟踪将要采取的行动将变得更加困难。
您认为哪种方法更适合GUI建模。你知道其他可能对我的问题更好的陈述吗?
答案 0 :(得分:3)
这是第三种选择:
symbol
(见下文)OnEvent
方法,该方法会返回symbol
从StateMachine::OnEvent(...)
事件转发到State::OnEvent
,返回symbol
- 执行结果。 StateMachine
然后根据当前状态和返回的符号决定是否
OnExitState
和OnEnterState
3个状态和3个符号的示例矩阵
0 1 2
1 2 0
2 0 1
在此示例中,如果机器处于任何状态,状态(0,1,2)
和State::OnEvent
将返回符号0
(矩阵中的第一行) - 它将保持相同的状态
第二行表示,如果当前状态为0
且返回的符号为1
,则转换为状态1
。对于州1
- >州2
和州2
- >州0
。
类似的第三行表示对于符号2
,状态0
- >州2
,州1
- >州0
,州2
- >州1
这就是:
symbols
的数量可能远低于州的数量。DB_ERROR
与NETWORK_ERROR
区别对待时,您只需更改过渡表并且不要触及状态实施。答案 1 :(得分:1)
我不知道这是否是您期望的那种答案,但我会用一种直接的方式来处理这些状态机。
使用枚举类型的状态变量(可能的状态)。在GUI的每个事件处理程序中,测试状态值,例如使用switch语句。做相应的处理,并设置状态的下一个值。
轻巧灵活。保持代码规则使其可读和“正式”。
答案 2 :(得分:1)
我个人更喜欢你说的第一种方法。我发现第二个是非常反直觉和过于复杂的。为每个状态设置一个类很简单,如果你在OnEnterState中设置正确的事件处理程序并在OnExitState中删除它们,你的代码将是干净的,并且所有内容都将自动包含在相应的状态中,以便于读取。
您还将避免使用巨大的switch语句来选择正确的事件处理程序或过程来调用,因为状态所做的一切在状态内部完全可见,从而使状态机代码简短。
最后但并非最不重要的是,这种编码方式是从状态机绘制到您将使用的任何语言的精确翻译。
答案 3 :(得分:0)
对于这种代码,我更喜欢一种非常简单的方法。
switch
语句或if
链中的复合块,并设置下一个状态。这样就没有额外的状态机管理元数据结构,也没有管理该元数据的代码。只是您的业务数据和转换逻辑。并且操作可以直接检查和修改所有成员变量,包括当前状态。
缺点是您无法添加本地化为单个状态的其他数据成员。除非你有很多州,否则这不是一个真正的问题。
如果您总是在进入每个状态时配置所有UI属性,而不是对先前设置进行假设并创建状态退出行为以在状态转换之前恢复不变量,我发现它还会导致更强大的设计。无论您使用什么方案来实现转换,这都适用。
答案 4 :(得分:0)
您还可以考虑使用Petri网对所需行为进行建模。如果您想要实现更复杂的行为,这将是更可取的,因为它允许您准确确定所有可能的方案并防止死锁。
此库可能对实现控制GUI的状态机很有用:PTN Engine