状态机

状态机的概念和图形表示

状态机是一种行为,它说明了对象在其生命期中响应事件所经历的状态序列以及它们对那些事件作出的反应。

相较于交互来说,交互是对一些共同工作的对象群体的行为建模,而状态机是对单个对象的内部行为建模。

从状态机的定义来看,它涉及到状态以及状态之间转移,这些也可以从状态机的图形表示可以看出: 状态机
我们使用圆角矩形表示状态,矩形之间的箭头表示状态的转移,转移之上标注事件。 一个状态机就是通过状态和转移对其所要描述的行为建模的,在图形上就是矩形与箭头的连接。

状态

状态是指对象生命期中的条件或状况,在此期间对象将满足某些条件、执行某些活动或等待某些事件。

状态通过圆角矩形来表示,每个状态都有一个区分于其他状态的名称,有时状态也可以匿名,即没有名称。 图示如下: UML 状态

对象只在某个状态下逗留有限的时间,所以一个对象会经历很多个状态,此时,我们通过状态机来描述对象所经历的状态序列。

深入状态的内部,我们可以发现,状态涉及一些常见部分,它们可以直接在矩形内部表示: 高级状态和转移

  1. 进入/退出效应 有时对象进入一个状态或者退出一个状态会有一些常规的操作,比方说,进入状态时做一些初始化的设置,而在退出状态时做一些清理工作;此时,上图中对这种惯用手法就提供了一些简洁的表示方法。
  2. do 活动 当对象处于一个状态时,它一般是空闲的,在等待某个事件发生。但有些时候,对象处于某个状态,会持续的做着某些工作,并一直继续直到被某些事件中断。 do 活动就描述了这种情况,即进入状态后,对象一直持续的做着某些工作,如果某个事件发生导致了一个离开当前状态的转移,那么当前状态中进行的任何 do 活动都会被立刻终止。
  3. 延迟事件 延迟事件是指某个状态中被延迟处理的事件,直到另一个状态被激活才被处理。 延迟事件的实现需要一个内部事件队列,如果一个事件发生,并被归为延迟事件,则进入队列。一旦对象进入一个不延迟这些事件的状态,这些事件就会从队列中取走处理。
  4. 内部转移 它是转移的一种,指的是通过执行一个效应来响应事件,但不改变状态,而且不执行进入和退出动作。还有一种转移叫做自身转移,与内部转移类似,转移后状态没有改变;但是自身转移会执行进入和退出动作,只是重新进入了原来的状态。

如果我们从另外一个角度看待状态的内部,我们会发现,状态之中还会嵌套状态,这种被嵌套的状态被称为子状态,而嵌套子状态的状态称为组合状态

组合状态内部有两大类型的子状态,非正交子状态(顺序的)和正交子状态(并发的)。

非正交子状态

非正交子状态是指组合状态中一些不相交的子状态,这些子状态的转移是顺序的,对象一次只能处于组合状态中的一个子状态。图示如下: 非正交子状态
在Active组合状态中,嵌套了一些非正交子状态,它们都是独立的,彼此不相干扰,并且可以激活转移。
接下来,我们来看看从外部状态进入和退出组合状态的情况:

  • 首先,我们来看从外部源状态进入组合状态。我们可以把组合状态当做转移的目标,也可以把组合状态中的子状态当做目标。此时,如果组合状态为目标的话,那么组合状态中一定有一个初始状态,从而使得进入组合状态(有进入动作则执行之)后可以把控制权交给初始状态;如果以组合状态内部的子状态为目标,那么会执行组合状态的进入动作和子状态的进入动作,并将控制权交给子状态。
  • 最后,从组合状态离开时,可能以组合状态或者其子状态为源状态。但无论是哪种情况,控制都是先离开子状态(有退出动作时则执行之),再离开组合状态(有退出动作则执行之)。离开组合状态,本质上是切断了内嵌状态机的活动。

另外,关于非正交子状态还有一种建模场景,那就是如何记录历史状态。状态机描述了对象的动态方面,它的当前行为依赖过去。我们可以通过历史状态来记住离开组合状态之前最后活动的非正交子状态。如果想用一个转移来激活最后的子状态,就显示从该组合状态之外直接指向该历史状态的一个转移。

历史状态根据状态的嵌套层级分为浅历史状态和深历史状态,通过包含 H 符号的小圆圈来表示浅历史状态,浅历史只记住直接嵌套的状态及历史;深历史将在任何程度上记住最深的嵌套状态。图示如下: 历史状态

正交子状态

正交子状态是指在一个组合状态中会分成一些正交的区域,你可以用这些区域来说明在一个对象的上下文中并行执行两个或多个状态机。 正交子状态的图形表示如下: UML 中的正交子状态 从图中可以看到,在一个组合状态中有两个正交区域,当对象进入组合状态中,控制流分岔为两个并发流,等到这些并发流都达到最终状态或者有一个离开组合状态的显示的转移,那么这些并发流就重新汇合为一个流。所以,一个对象在一个时间可能同时处于多个正交子状态,而只能处于一个非正交子状态;即正交子状态是并发的,非正交子状态是顺序的。

正交子状态的分岔与合并可以通过更详细的图形来表示: 分岔与汇合

转移

状态机描述了对象生命期中经历的状态序列,转移表示了序列中两个状态之间的一种关系,它指明对象在第一个状态中执行一定的动作,并当特定事件发生或特定的条件满足时进入第二个状态。 当状态发生这样的转变时,我们就称转移被激活了;在转移激活之前,我们称对象处于源状态,激活后对象处于目标状态。 一个转移的激活涉及到事件、监护条件和效应。如果一个对象识别了事件,则在监护条件满足的情况下激活转移,转移激活时会执行一些效应,它可以直接作用于拥有状态机的对象,并且间接作用于该对象可见的其他对象。

事件

事件就是指事情的发生,是对一个在时间和空间上占有一定位置的有意义的发生的定义。

事件可以根据系统的内外部划分为内部事件和外部事件,在外部参与者与系统之间发生的事件我们就称之为外部事件,而在系统内部的部分之间发生的事件,我们就称之为内部事件。

事件可以分为四类:

  1. 信号事件。信号是一种异步事件,属于类目中的一种,它和简单类很相似,可以拥有属性和方法。它的实例被称之为消息,消息由一个对象异步发出,另一个对象接收。如交互一节中所描述的,消息还会期望一些动作。
  2. 调用事件。就像信号事件代表一个信号的发生一样,调用事件表示一个对象接收到一个操作调用的请求。通常,调用事件都是同步的,调用者期待一个返回;如果调用者不期望返回,那么调用事件也可以是异步的。
  3. 时间事件。类似于定时器,是指随着时间推移而发生的事件。在视觉表示上可以表现为 “at (绝对时间点)”或者 “after (相对时间)”,如下图所示: 时间事件
  4. 变化事件。是指状态的一个变化或某些条件得到满足的事件,其表示如上图所示,通过“when (布尔表达式)”来描述需要满足的变化条件。
监护条件

监护条件是一个布尔表达式,当一个事件触发使得转移可能被激活时,就对该布尔值求值。如果取值为真,则激活转移;如果取值为假,并且没有其它的转移能被这个事件触发时,那么这个事件就会丢失。

监护条件在图形表示上由一个方括号括起来的布尔表达式表示,放在事件触发器后面。

监护条件只在引起转移的事件触发器之后才会计算,并且只计算一次。

效应

效应就是转移激活时所执行的行为,可以包括在线计算、操作调用(调用拥有状态机的对象或其他可见的对象)、另一个对象的创建和撤销,或者向一个对象发送信号。

状态一节中,我们提到的进入/退出效应属于效应的一种。一个转移的效应以及任何相关的进入/退出效应都必须执行完毕,才允许另外的事件引发新的转移;也就是说,效应的执行是转移的一部分,在效应没有结束执行之前,不会引发新的转移。

转移通过一条从原状态指向目标状态的有向实线表示,如图所示: UML 转移

从上图中,我们可以看到事件、监护条件和转移,其中还有一个叫做“自身转移”。自身转移是一种特殊的转移,在状态一节中,我们将自身转移与内部转移做过比较,自身转移是指目标状态为原状态的转移。

用户头像
登录后发表评论