2011-02-08 66 views
38

我在编程经验中遇到了一些状态模式的实现,并做了一些工作。我看到他们在各种场景中使用(主要是UI和解析)。问题在于,它们都在快速发展的压力下变成了几乎不可维护和易于理解的代码块。我正在考虑重构其中之一,但我无法为此在线找到好的资源。在线状态模式有很多简单的例子,但我需要更多深入的资源。如何正确使用状态模式?

所以我在寻找:常见的错误

  • 例子时 实施状态模式以及 如何避免它们,
  • 现实世界做正确 状态模式的例子(如在 一些开源项目/框架)
  • 个人经历 模式也欢迎

谢谢你的时间

+0

如果您拥有很多状态,例如在3D游戏中,则不适用。 – 2011-02-14 21:41:31

+0

那么你建议什么样的选择? – Ivan 2011-02-15 14:01:14

+0

如何链接到状态模式的定义? – 2011-02-16 22:52:46

回答

22

@Ivan:在网络上有大量资源可用于Hierarchical State Machines(HSM)。 Miro Samek已经撰写了大量有关这种设计模式的文章,并提供了大量有用的信息。

一些文章,应该会感兴趣:

在平面FSM状态图上使用HSM的最大好处是描述Mealy和Moore的床位是这种等级制造了责任分离。子状态只需要处理它们明确设计要处理的条件 - 未处理的事件会传递到父状态,如果父状态没有明确设计来处理它,则它会传递到下一个更高的状态父母等等。它允许您创建小型,可管理的状态机,每个状态机都可以用于单一目的 - 可以放在单个对象中。随着新功能的添加或新增类别的添加,他们只需要处理他们自己的一小部分世界并将未处理的事件传递给其各自的父母。

正确实施后,您将获得一个具有较低圈复杂度的强大程序,可以根据需要轻松修改或升级。

7

只是我的2美分,国家模式总是变得难以维持,因为它是很难理解谁没有编码的人。我通常回退到旧的标准的函数/方法指针数组,就像我以前的C经验一样。您只需构建一个具有状态/信号的线/列的二维数组函数指针。更容易理解。有了管理这一点,你委托给其他类来处理复杂的一类...

MY2C

+2

+1有趣的想法...你有样品吗? – 2011-02-08 17:10:39

6

大多数时候,各州的状态模式设计的正在处理一个以上的国家(或子状态国家),这使得他们难以维持。

如果一个国家在那里有任何一种选择,它主要处理多个国家。

我需要很多纪律来保持州的清洁。

一个可能的解决方案是让状态机本身更复杂(HSM)。 这使得它在更高级别更具可读性,因为它必须处理更少的状态。

4

看一看Finite State Machine。几乎每一种成熟的语言都有自己的好例子。既然你没有指定你的首选语言,我会给你C++的例子:Boost FSM library。很可能它比你需要的复杂得多,但它可以给你一些肯定的设计提示

1

如果你对每个状态有不同的行为,你应该使用状态模式。也许你需要在运行时重新配置转换。使用它的另一个原因是您稍后可能需要添加更多状态。

想象一下,像中国跳棋这样的棋盘游戏,您可以选择不同的GUI状态来选择一个Pawn,选择一个目标插槽等等。在每种状态下,图形用户界面的行为应该不同,某些输入应该被别人忽略。使用一个简单的开关/外壳是可能的,但是随着逻辑被封装,状态模式变得方便,相关的代码就不在话下了。这使得在不影响大多数/所有其他状态的情况下(取决于负责设置转换的状态:状态知道其外出转换,或者可以在运行时例如使用构造器)可以更容易地引入新状态。

正如您在this example中看到的,GuiController使用IGuiState接口来根据需要更改其行为。一个实现can be seen here

当你需要灵活时,主要的缺陷是使用开关/外壳。由于间接需要多一点时间,我建议对于固定数量的相当简单的staates。我必须实现一个相当快速的低级网络协议,这通常需要很多开销。

23

正如你可能已经读过的那样,State Design Pattern在状态改变其组成包含该状态的某个对象的行为时很有用。这意味着State抽象类,接口或enumerated type的概念 - 尽管取决于语言Duck Typing也可以 - 它定义了任何常见行为和/或所需方法。

主要方面

实际上有符合国家模式的工作,需要考虑两个主要方面:枚举和过渡。枚举仅仅意味着识别一组可能的状态(例如一周的几天),或者更抽象地指定工作流引擎的开始,结束和中间状态的类型(即元状态)。转换意味着决定如何为运动建模这通常是通过捕获表格表示中的所有可能的转换(即Finite State Machine)或使每个状态知道其可能的“转换”到其他状态来完成的。

通常过渡齐头并进与元状态,因为它不可能知道所有的状态和关系提前在新的状态,从而转换,可以在运行时添加这样的动态系统。另外,通过转换方法,某些行为(例如通知)成为转换的一部分,而不是状态本身。

例子

有几种方案,我无论是在工作或讨论,其中,这是一个利用设施:

  1. 工作流程
  2. 计算机比赛的对手A.I.
  3. 流程编排

通过工作流程我的意思是这样jBPM。像这样的系统关心的是在正确的时间对合适的人进行正确的关注。他们通常会发送大量电子邮件或其他类型的通知。而且,他们所代表的流程需要随组织变化而变化的能力,而被管理的数据通常变化更慢。

电脑游戏对手A.I.是不言自明的。不是我写的东西,但是在与那些拥有这些东西的人的对话中,这些系统通常是自我包含的。换句话说,与工作流程不同,游戏通常不具备改变用于控制计算机对手的过程的能力。

流程编排类似于工作流程,但侧重于系统集成,而不是人员交互。 Apache Mule框架就是一个例子。这里状态可以描述状态(例如启动,进程中,结束)和类型(例如,ftp集成点,sql集成点)。

结论

不像其他的答案,我觉得状态封装是管理软件系统变化的好方法。做得很好,它可以促进这些更改或使用户在运行时可以这样做。为了提高实现的复杂性,您需要权衡更多的灵活性。因此,这种方法可能对购物车没有用处,例如行为可能非常知名且不想改变。另一方面,当流程可能发生变化时,它会非常合适。

0

我正在构建一个能够评估元素集的表达式评估器。我发现状态模式对于根据状态区分什么可以和不可以做什么对于一个集合非常有用。即:开放,关闭,无效,有效等。 FSM非常容易绘制,并且通过消除大量ifelse语句块来定义该功能应根据其所包含的属性执行什么操作来减少代码的复杂性。它通过将条件变为类来使这些条件更加明确。这是迄今为止我最喜欢的模式之一。

2

所以我在寻找:贯彻执行国家的模式,如何避免他们时常见的错误

  • 例子,

状态模式不能很好地扩展。试想一下有10个状态和10种不同转换类型的状态机。添加新状态意味着状态必须定义全部10个转换。添加新的转换意味着所有的10个州必须定义它。简而言之,如果你的状态机不稳定和/或你有很多状态/转换,不要使用状态模式。

  • 现实世界(在一些开源项目/框架等)做正确的状态模式的例子

定义正确 :-)在https://stackoverflow.com/a/2707195/1168342引用Java的例子是JSF生命周期,但我认为只有一个转变。没有其他答案引用任何国家。

  • 与状态模式的个人经验,也欢迎

Head First Design Patterns uses a Gumball machine example说明国家。这很讽刺,但每次扩展设计(添加新状态或转换)时,都会有很多重复的代码(尤其是特定状态下的无效转换)。另外,根据谁决定下一个状态是什么,各个状态类可以彼此耦合(状态间依赖关系)。请参阅本答案末尾的解释:https://stackoverflow.com/a/30424503/1168342

GoF书中提到table-based alternatives有优势,即它们的规律性。更改转换条件需要更改表格(而不是代码)。