我正在尝试使用 SML 实现状态机,并且我有一个关于特定场景的最佳实践的问题。
假设状态机代表一个可以根据用户输入打开和关闭的门,但只有在安全时才关闭(用户不在门附近)。
因此,为了关闭门,我需要有一个用户命令来关闭,并且用户清除门。
后者(门清除)对我来说似乎是一个事件,但后来我遇到了一个问题,我正在尝试实现从
open
到 closing
的状态转换,需要两个事件(close
和 userClear
)。我不知道这两个事件会以什么顺序发生,userClear
甚至可能在进入状态open
之前发生,或者永远不会触发,因为用户从来没有离大门太近。
在 SML 示例中,我发现可以推迟事件。也许这有点用处?
我还可以将清除状态编码为状态。这将增加我拥有的状态数量,从而产生一个像
这样的转换表openNotClear + clear = openClear,
openClear + close = closingClear,
openNotClear + close = openWaitingForClear,
openWaitingForClear + close = closingClear,
...
这似乎并不理想,因为状态空间很快就会变得很大。
我确信这种情况并不少见,并且有一个标准方法来处理此类多个事件/条件。
在事件驱动的状态机中如何最好地处理这个问题?
如果您使用的框架允许,建议使用并行状态。
此外,在这个答案中,我将考虑这样一个事实:当您的
close
事件被触发时,门应该尽可能关闭,而不需要任何其他 close
事件。
因此,除了门有两种以上的状态之外别无选择:
Gate State Machine
Closed + open = Opened
Opened + close = AboutToClose
AboutToClose + userClear = Closed
现在,让我们想象一些并行状态(或并行运行的第二个状态机)代表大门周围的业务,具体取决于玩家的接近度:
Proximity State Machine
Clear + userComing = Busy
Busy + userLeaving = Clear
现在,您需要添加两个额外的检查才能最终实现:
这看起来像这样:
func1: onAboutToCloseEntered:
if (proximity.state == Clear)
generateEvent(userClear)
func2: onClearEntered:
generateEvent(userClear)
如果进入 Gate 状态机的“AboutToClose”状态时,Proximity 状态机的状态为
Busy
,则不执行任何操作 (func1
)。门状态机将保持在 AboutToClose
状态。稍后,当用户离开大门时,将生成事件 userClear
(func2
)。
如果在
AboutToClose
状态下需要再次打开门,则只需添加一个新的转换到 Opened
状态即可。