状态

State(状态)对状态机行为执行过程中满足一些不变条件的场景进行建模。在大多数情况下这种条件不是显式定义的,而是通常通过与该状态相关联的名称来暗示的。例如,在图14.36中,对电话单元的行为进行了建模,状态“Idle”和“Active”分别代表了电话没有和正被使用的场景。这个例子还展示了状态并非要代表一个完全静态的场景,因为在“Active”状态的上下文中很显然有一些细节活动出现。然而,贯穿该活动始终,电话仍旧是在被使用(即,“active”)。

状态种类

如下种类的状态是被区分的:

  • 简单状态(isSimple=true
  • 组合状态(isComposite=true
  • 子状态机状态(isSubmachineState=true

简单状态没有内部的顶点或转变。组合状态至少包含一个区域,而一个子状态机状态引用了整个状态机,它从概念上是“内嵌”到该状态。一个组合状态可以是一个简单的组合状态(只有一个区域)或是一个拥有多个区域的正交状态(isOrthogonal=true)。例如,图14.9中的“CourseAttemp”状态是一个只有一个区域的组合状态,而“Studying”是一个包含三个区域的组合状态。

被组合状态的一个区域包含的任意状态都称为该组合状态的子状态。如果它们没有被其它状态包含,那么它们就称为“直接子状态”,否则被称为“间接子状态”。

状态配置

通常,一个状态机可以有多个区域,每一个都包含它自己的状态,其中的一些可能是拥有多个区域的组合状态,等待。从而,一个执行的状态机实例的一个特定“状态”要通过一个或多个状态层级来表示,从状态机最顶层的区域开始通过组合层级向下直到简单的叶子状态。类似的,我们可以在一个组合状态中讨论一个子状态层级。状态的这种复杂层级被称为(状态或状态机的)一个“状态配置”(state configuration)。举个例子,图14.9中所描绘的状态机执行的一个有效状态配置是:<CourseAttempt - Studying – (Studying::Lab2, Studying::TermProject, Studying::FinalTest)>。一个执行的状态机实例每次只能正好处于一个状态配置,该状态配置称为它的活跃状态配置。在对与状态机的触发器匹配的事件出现的响应下,状态机执行被表现为从一个活跃状态配置到另一个活跃状态配置。

如果一个状态是活跃状态配置的一部分,那么它就是活跃的。

当满足如下条件是,一个状态配置被称为是稳定的

  • 从这个状态配置没有进一步的转变可以使能,并且
  • 配置中的所有entry行为(如果有的话)都执行完毕(但是不包含doActivity行为(如果定义了的话),它们可能能在执行)。

在被创建和执行完它的初始转变后,状态机总是处于一些状态配置。然而,因为状态可能是层级的并且转变和状态可能关联行为,因此,“进入”一个层级化的状态配置涉及一个动态的过程,该过程只会在达到一个稳定的状态配置后才停止。这为精确的指明状态机何时位于一个状态配置的某个特定状态制造了一些潜在的模糊性。状态机何时被认为“处于”一个状态和“离开”一个状态的规则分别在“进入一个状态”和“离开一个状态”部分进行描述。

一个配置被视为是稳定的即使在状态机的事件池中有延迟的、完成(事件)的或其它未决的事件类型。

状态entry、exit和doActivity行为

状态可以关联一个entry(入口)行为。如果定义了这个行为,它会在状态通过一个外部的转变被进入时执行。此外,状态还可以关联一个exit(离开)行为,如果定义了这个行为,它会在状态被离开时执行。

状态还可以关联一个doActivity行为。该行为在状态被进入(但要在状态的入口行为执行结束后)时开始执行,并且与该状态关联的其它行为并发执行,直到:

  • 它结束(此时产生一个完成事件),或者
  • 状态被离开,此时,doActivity行为的执行被放弃。

一个状态的doActivity行为的执行不受该状态的内部转变发生的影响。

状态历史

状态历史(history)的概念是由David Harel在最初的状态图形式模型中引入的。它是与组合状态的区域相关的一个方便的概念,区域可以借助历史跟踪它上一次退出时所位于的状态配置。这使得返回哪个相同的状态配置变得容易,如果愿意,下一次区域变为活跃时(例如,从处理一个中断返回),或者有一个返回到它的历史的内部转变。这可以通过将一个转变终止在该区域内部的一个期望类型的历史伪状态上来很容易地实现。该功能的好处是它消除了用户显式地跟踪历史的需要,这可以显著的简化状态机模型。

提供了两种历史伪状态。深度历史(deepHistory)代表了对区域最近一次访问的完整状态配置。它的效果就如同有一个转变终结在所保存状态配置的最深层的状态上,包括对沿途遇到的所有入口行为的执行。浅历史(shallowHistory)代表只返回到最近一次的状态配置的最顶层子状态,使用缺省入口规则进入。

当一个转变终结在一个历史伪状态上,并且该状态之前没有被进入过(即没有历史)或它已经到达了它的FinalState,那么可以使用缺省历史机制来强制一个转变终结在某个特定的子状态上。这个转变源自历史伪状态,终结于包含该历史伪状态的区域的一个特定顶点(缺省历史状态)。这个转变只在执行到这个历史伪状态且当前状态之前没有活跃过。否则,执行适当的进入该区域的历史入口(参见上面)。如果没有定义缺省历史转变,那么该区域的标准缺省入口按照如下的解释执行。

延迟事件

状态可以规定一组在本状态内被延迟的事件类型。这意味着只要该状态一直活跃,那么这些类型的事件出现就不会被派发,而是一直停留在事件池,直到:

  • 到达一个这些事件类型不再被延迟的状态配置,或者
  • 某个延迟的事件类型被显式地应用在一个转变的触发器中且该转变的源为该延迟状态(即,一种重载)。

事件可能被一个组合状态或子状态机状态所延迟,此时,只要该组合状态仍处于活跃配置,那么该事件就一直被延迟。

进入一个状态

进入一个状态的语义依赖于状态的类型和它被进入的方式。然而,所有情况下,在进入时状态的入口行为被执行,但只在与进入转变相关联的任何effect行为执行结束之后。而且,如果该状态定义了一个doActivity行为,那么在其入口行为执行完毕后,doActivity行为立即开始执行。它与进入该状态相关联的后续行为并发执行,例如作为相同复合转变一部分而被进入的子状态的入口行为。

上面的描述全面地覆盖了简单状态的情况。对于具有单个区域的组合状态,还有如下的状况存在:

  • 缺省进入:这种场景出现在该组合状态是一个转变的直接目标的时候(在图形上,这体现为一个到来的转变终结在该组合状态的外边上)。在执行完入口行为以及分出一个分支执行可能的doActivity行为之后,如果定义了一个初始伪状态,那么将经由该伪状态节点通过它的流出转变(称为该组合状态的缺省转变)持续进行下去。如果没有定义初始伪状态,那么就没有定义一种方法(可以进行下去)。一种处理是把该模型认为是错误的;另一种处理时把该组合状态当做一个简单状态,终结其在内部的遍历。
  • 显式进入:如果到来的转变或它的continuations终结在了该组合状态的一个直接子状态上,那么该子状态变为活跃并且它的入口行为在所属组合状态的入口行为执行完毕后执行。这个规则递归地应用到终结在间接(深度内嵌)子状态的转变上。
  • 浅历史进入:如果到来的转变终结在该组合状态的一个区域的一个浅历史伪状态上,那么活跃的子状态称为该次访问之前最近一次活跃的子状态,除非:
    • 最近一次的活跃子状态是FinalState,或者
    • 这是该状态的第一次被访问。
    • 在上述两者情况下,如果从该浅历史伪状态定义了一个缺省历史转变,那么就使用它。否则,应用缺省的状态进入。
  • 深度历史进入:除了目标伪状态的类型是深历史并且需要递归的将规则应用到该活跃状态的所有层次上外,它与浅历史的规则是相同的。
  • 入口点进行:如果转变通过一个entryPoint(入口点)伪状态进入一个组合状态,那么与该入口点的流出转变相关联的effect行为执行并穿过该入口点伪状态(但是在该组合状态的入口行为执行之后)。

如果组合状态是一个具有多个区域的正交状态,它的每个区域都会被缺省或显式地进入。如果转变终结在组合状态的边上(即没有进入该状态),那么所有的区域使用上面所说的缺省进入规则来进入。如果转变显式地进入一个或多个区域,那么这些区域被显式进入,其它的被缺省进入。

不管状态是如何被进入的,状态机已经认为处于这个状态,即使这个状态的任何入口行为或effect行为(如果定义的话)还没开始执行。

离开一个状态

当离开一个状态的时候,不管它是简单的还是组合的,离开所涉及的最后一步(所有其它与离开相关的行为执行完毕后)是执行该状态的exit(离开)行为。如果状态有一个doActivity行为在该状态离开时仍在执行,那么这个行为在离开行为开始执行之前被终止。

当从一个组合状态离开时,离开是从当前活跃状态配置的最深层状态开始的。如果离开是通过一个exitPoint(离开点)伪状态开始的,那么状态的离开行为在执行完终结在该离开点上的转变的effect行为之后执行。

当从一个正交状态离开时,它的所有区域都被离开。此后,该状态的离开行为被执行。

不管状态是如何被离开的,只有在该状态的离开行为(如果定义的话)执行完毕后,状态机才被认为是离开的这个状态。

被封装的组合状态

在一些建模场景中,对一个组合状态进行封装很有用,通过不允许转变直接穿过该状态到达它的内部节点来实现(对此一个通常的用例是一个状态的内部是抽象分类符,期望在不同的子类精化中被规定)。在封装中,通常需要对组合状态的内部元素与到来或流出的转变进行绑定,这通过入口点离开点伪状态来实现。

入口点代表了到来转变的终结点和终结在组合状态的一个内部顶点上的转变的起始点。在效果上,后者是外部到来的转变的一个continuation,并且该组合状态的入口行为(如果定义的话)发生在到来转变的effect行为和流出转变的effect行为之间。如果没有定义流出转变,那么简单地执行状态的缺省进行。

离开点是入口点的对立面。也就是说,来自该组合状态内的一个顶点的转变可以终结在离开点上。在一个良构的模型中,这样的一个转变应该有一个相应的外部转变从相同的离开点流出。如果该组合状态定义了一个离开行为,它在离开点到来的转变上的所有effect行为执行完后、在外部流出转变上的任何effect行为执行之前执行。

子状态机状态和子状态机

子状态机是状态机规约可以被多次复用的一个手段。它与被封装的组合状态类似,也需要把到来和流出的转变与其内部的顶点进行绑定。然而,被封装的组合状态和它的内部元素是定义在所属的状态机内的,而子状态机就像编程语言中的宏,是不同的行为规约,可能定义在不是所使用的上下文中。因此,它需要更加复杂的绑定。这通过子状态机状态(即,isSubmachineSate=true的状态)的概念来完成,它代表了对相应的状态机的引用。连接点引用的概念用于支持子状态机状态与所引用的状态机之间的绑定。连接点引用代表了子状态机状态上的一个点,转变可以在此到达或流出。每个连接点引用与所引用的状态机的相应入口或离开点相匹配。这为子状态机调用和它的规约之间提供了一个必需的绑定机制。

子状态机状态暗示了把相应的状态机规约像宏一样插入。因此,语义上等同一个组合状态。被引用的状态机的区域是该组合状态的区域。入口离开effect行为以及内部的转变都包含在此子状态机状态中。

注意. 每个子状态机状态都代表了一个子状态机的不同的实例化,即使是两个或更多子状态机状态引用相同的状态机。

被引用的状态机通过通过它的缺省(初始)伪状态或它的入口点来进入。通过初始伪状态进入与一般的组合状态相同。入口点与junction伪状态(在组合状态是正交时是fork)等价:通过入口点进入意味着组合状态的入口行为被执行,跟着的是从入口点到组合状态中目标顶点的转变。为使规约是良构的,与入口点转变关联的guards必须为真。

类似的,被引用的状态机可以作为如下的结果而被离开:

  • 到达了它的FinalState,
  • 源自子状态机状态的组转变的触发,或者
  • 通过它的任离开点。

通过FinalState或一个组转变与普通的组合状态相同。

results matching ""

    No results matching ""