状态模式和守卫模式

13

更新:

使用状态模式可能不是解决这个问题的正确方法。因此,欢迎使用任何其他模式。基本上,我正在寻找一种方法来为每个状态设置保护条件,同时拥有干净且易于维护的代码。像emberjs、ui-router和react-router这样的前端路由系统如何实现“保护条件”,以避免在不满足条件的情况下进入特定状态?


我想使用状态模式实现有限状态机,但我无法理解它。简单说就是:

If error -> error state
If A && B && C -> second state
If only A -> first state
在任何状态下,出现错误时,我们都进入错误状态。输入事件A、B和C可能以任何顺序到达,但如果它们全部通过,我们就会进入第二个状态。如果只有输入A适用,则我们会进入第一个状态。
以下状态图摘自Martin Fowler的领域特定语言书籍。
他在描述中说:
“Grant小姐在她的卧室里有一个通常被锁上和隐藏起来的秘密隔间。要打开它,她必须关闭门,然后打开她的第二个抽屉并打开床头灯(无论先后顺序如何)。完成这些步骤后,她就可以解锁秘密面板。”
我强调,“打开床头灯”和“打开第二个抽屉”可以以任何顺序发生,与A、B和C同理。
基于@SQLPolice的评论和该书,我画了这个:
但问题是,我可能拥有(A && B && C && D && D && E)。在这种情况下,拥有所有组合中间状态会很麻烦。

你有一个起始状态吗? - SQL Police
你是用什么来画图的……UML/流程图? - David Barker
@DavidBarker,都不需要。我只需要一些可视化的东西来理解如何实现。 - Sam R.
好的,但是当你只有 A 时,你想要进入不同的状态。但是如果 A 是第一个事件呢?你怎么知道 B 或 C 会接下来发生什么? - SQL Police
哦,这是一个不错的图画!- 是的,你说得对,当变得更加复杂时,它会变得非常麻烦。这就是为什么你要使用工具来创建这样的状态机 - 例如 lexflex等(用于词法分析)。它们会创建带有许多标签和 goto 跳转的 C 代码... - SQL Police
3个回答

4
你可以使用某种形式的词法分析来实现。我会通过限制从一个状态转换到另一个状态的能力,除非满足两个状态之间边缘上放置的约束条件来处理此问题。我最近在Laravel框架中用PHP编写了一个有示例的FSM,其中各种约束条件都需要满足才能发生过渡。它在状态内使用伪状态或处理程序来切换标志,指示进程已完成。只有当所有标志都设置为true时,状态才会转换。

Sample lexical state analysis

使用我为laravel编写的FSM包,一个示例FSM设置会类似于这样
每个状态都会(通过onEnter或伪状态)将其约束标志设置为true,从而触发FSM或状态上的checkReady(),以根据约束标志触发转换或保持当前状态。
添加新约束是将它们添加到状态或包含FSM中的约束数组,并构建一个方法以允许在执行任务时删除约束的情况。
当您查看多个状态时,每个状态都形成对约束的要求。样例状态看起来会像这样。 当您查看具有伪状态/处理程序的单个状态时,状态看起来会像这样,其中包含其逻辑。

3
一个快速的草图看起来是这样的:

enter image description here


谢谢,但我该如何避免写if (A && B && C)呢?原因是,我已经有了很多带着各种条件的if和else。选择状态机的唯一原因是为了避免这样做。 - Sam R.
啊,我明白了。这背后是什么 - 这是学校作业吗,还是你想实现什么? - SQL Police
这不是作业。我比这种事情更年长。这是我们真正面临的问题,我们正在尝试简化事情。 - Sam R.
这是一段时间以前的事情,但我大致记得如何通过词法分析来完成。您可以使用中间状态序列来解决"A&B&C",像这样:如果您有A,那么切换到中间状态1。现在,在那里,如果您有B,则转到中间状态2,但如果没有,则转到最终状态1。在中间状态1中,如果您有C,则转到最终状态2。这是一种可能的方式,就像自动化工具会执行的方式。 - SQL Police
创建一个转换事件 D,其中 D = (A && B && C) - Fuhrmanator

1
一个状态机抽象由以下组成:
  1. 状态
  2. 事件或输入
  3. 转换
  4. 动作
类似 a&&b&&c 这样的语句实际上是一个事件或输入……一个转换的标签。因此,如果要适应状态机抽象,就需要将其映射到一个事件上。您需要编写代码来进行这种映射。
如果您的状态机通常由这些条件驱动,您需要在 a、b 和 c 发生变化时挂接事件,或者在定时器上定期检查它们。每当它们发生变化时,您的代码就会将它们映射到一个事件上,并将它们发布到推进状态机的任何代码中。

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