状态图

来自术语
跳转至: 导航搜索

    又称状态机图(state machine diagram)或状态转移图(state transition diagram)。描绘系统或其部件所能经历的状态,以及状态之间如何转移的图。

属性[编辑]

用于 显示状态机 基于 事件反应
动态建模 受事件驱动的方面 英文名 state diagram
目的 研究类、角色 中文名 状态图

基本介绍[编辑]

状态图[1] 用于显示状态机(它指定对象所在的状态序列)、使对象达到这些状态的事件和条件、以及达到这些状态时所发生的操作。

动态建模[编辑]

状态机用于对模型元素的动态行为进行建模,更具体地说,就是对系统行为中受事件驱动的方面进行建模(请参见概念:事件与信号)。状态机专门用于定义依赖于状态的行为(即根据模型元素所处的状态而有所变化的行为)。其行为不会随着其元素状态发生变化的模型元素不需要用状态机来描述其行为(这些元素通常是主要负载管理数据的被动类)。

图 1:状态机符号。状态机由状态组成,各状态由转移链接在一起。状态是对象执行某项活动或等待某个事件时的条件。转移是两个状态之间的关系,它由某个事件触发,然后执行特定的操作或评估并导致特定的结束状态。图 1 描绘了状态机的各种元素。一个简单的编辑器可被视为有限的状态机,其状态为Empty(空)、Waiting for a command(等待命令)和 Waiting for text(等待文本)。事件 Load file(装载文件)、Insert text(插入文本)、Insert character(插入字符)和Saveandquit(保存并退出)导致了状态机中的转移。下面的图 2 描绘了编辑器的状态机。 图 2:简单编辑器的状态机

状态[编辑]

状态是对象执行某项活动或等待某个事件时的条件。对象可能会在有限的时间长度内保持某一状态。状态具有以下几项特征:

历史状态[编辑]

除非另 图 4:历史状态有指定,当转移进入复合状态时,嵌套状态机的操作将从初始状态开始重新执行(除非转移直接以子状态为目标)。历史状态使状态机可以重新进入在它退出复合状态之前的最后一个活动子状态。图 4 显示了如何使用历史状态的示例。

建模技术[编辑]

状态机最多地用于建立对象在其生命期内的行为模型。当对象具有依赖于状态的行为时,尤其需要使用状态机。可能具有状态机的对象包括:类、子系统、用例、接口(以声明实现该接口的对象必须满足的状态)和协议(以声明实现该协议的对象必须满足的状态)。并非所有对象都需要有状态机。如果对象的行为很简单,只是存储或检索数据,那么该对象的行为就与状态无关,它的状态机也没有多少用处。

要建立对象生命期的模型,需要包括三个事项:指定对象可以响应的事件、指定对这些事件作出的响应以及指定过去行为对当前行为的影响。对象生命期的建模还涉及到确定对象有意义地响应事件的顺序,即从创建对象时开始,继续到该对象被破坏时为止。

要建立对象生命期的模型:

状态机的环境设置为类、用例或整个系统。

如果环境是类或用例,则要收集相邻的类,其中包括父类或通过关联关系或依赖关系可以接触到的类。这些相邻类是操作的候选目标,并且是可以包括在警戒条件中的候选目标。

如果环境是整个系统,则要将重点集中到系统的一个行为上,然后考虑在该方面涉及到的对象的生命期。整个系统的生命期通常会大得无法成为有意义的重点。

确定对象的初始状态和终止状态。如果初始和终止状态具有前提条件和后续条件,也应将这些条件定义出来。

确定对象要响应的事件。这些事件可以在对象的接口或协议中找到。

按照从初始状态到终止状态的顺序,列出对象可能处于的顶层状态。将这些状态与相应事件所触发的转移连接起来。然后添加这些转移。

确定所有进入操作或退出操作。

通过使用子状态来扩展或简化状态机

检查状态机中的所有事件触发转移是否与该对象实现的接口或协议所期望的事件相符。同样,检查对象的接口或协议所期望的所有事件是否都得到了状态机的处理。最后,确定要在哪些地方明确地忽略事件(如延迟的事件)。

检查状态机中的所有操作是否都得到了包含对象的关系、方法和操作的支持。

跟踪状态机,将它与事件及其响应的预期序列进行比较。搜索无法达到的状态以及状态机无法继续向前的状态。

如果要重新布置或重新构建状态机,需检查并确保语义没有发生变更。

提示与技巧[编辑]

当给定一项选择时,要使用状态机的可视语义,而不要写出详细的转移代码。例如,不要用几个信号触发一个转移,然后使用详细代码来管理以不同的方式依赖于信号的控制流。应使用由单独的信号来触发的单独转移。在隐藏了附加行为的转移代码中,要避免使用条件逻辑

根据在状态期间等待的事件或正在发生的事件来命名状态。记住,状态不是“时间点”;它是状态机等待某个事件发生的时间段。例如,“waitingForEnd”这一名称比“end”更好;“timingSomeActivity”比“timeout”更好。不要让状态的名称看起来象是操作名。

在一个状态机内唯一地命名所有状态和转移;这将便于进行源级别的调试。

谨慎使用状态变量;不要在创建新状态时使用它们。如果状态不多,很少带有或不带有依赖于状态的行为,并且很少有或根本没有可能与包含状态机的封装体并行或独立的行为,就可以使用状态变量。如果有复杂的、依赖于状态的潜在并行行为,或者如果必须处理的事件可能来自于包含状态机的封装体之外,则应考虑使用构件封装体。

如果单个图中的状态超过 5 * 2 个,就应考虑使用子状态。在这里可以应用我们的常识:在一个非常规则的模式中可以有十个状态,但如果两个状态之间具有四十个转移,显然就需要重新考虑了。务必要使状态机易于理解。

使用触发事件的事件和/或在转移期间发生的事件为转移命名。选择更加易于理解的名称。

当您看见一个选择点时,应考虑是否可以将作出该选择的职责委托给另一个构件,以便将其作为一组将不同的信号提供给封装体遵照执行(例如,代替对消息->数据 > x 的选择),并考虑是否可以让发送方或另一中间主角来作出决定,然后通过在信号名称中明确显示该决定的方式发送信号(例如,使用名为 isFull 和 isEmpty 的信号,而不是以值命名信号并检查消息数据)。

为在选择点中回答的问题指定描述性的名称,例如“isThereStillLife”或“isItTimeToComplain”。

在任何给定的封装体中,尽量使选择点名称保持唯一(其原因与转移名称需保持唯一相同)。

转移的代码段是否太长?是否应使用函数来代替它们,是否将常用代码段记录为函数?转移应该类似于高层的伪代码,并且应当遵循与 C++ 函数相同或更严格的长度规则。例如,代码超过 25 行的转移可被认为是过长。

应根据函数执行的操作来命名函数。

要特别注意进入和退出操作:在进行更改后忘记更改相应进入和退出操作的情况尤其容易发生。

退出操作可用于提供安全性功能,例如,从“heaterOn”状态中的退出操作将关闭加热器,在这里,操作被用来强制执行一个断言语句。

通常,除非状态机是抽象的并且将由包含元素的子类来进行改进,否则子状态应包含两个或更多个状态。

应该用选择点来代替操作或转移中的条件逻辑。选择点容易被看到,而代码中的条件逻辑则是不可见的,很容易被忽略。

避免使用警戒条件。

如果事件触发了几个转移,将无法控制首先对哪个警戒条件求值。这会产生无法预料的结果。

可能有多个警戒条件为“True”,但随后只能有一个转移。所选择的路径是无法预料的。

警戒条件是不可见的;要“看见”它们的出现更是困难。

避免使用类似流程图的状态机

这可能表示您试图对并不实际存在的抽象概念进行建模,例如:

使用一个封装体来对最适合于数据类的行为进行建模,或

通过使用紧密耦合的数据类和封装体类来对数据类建模(例如,数据类用于向四周传递类型信息,但封装体类包含了应与数据类相关联的大部分数据)。

状态机的这种错误用法可以通过以下故障现象来识别:

被发送给“自己”的消息,主要是为了重复使用代码

几乎没有状态,但有很多选择点

在某些情况下没有循环的状态机。在流程控制应用程序中,或者在试图控制一个事件序列时,这样的状态机是有效的;如果它们在分析过程中出现,则表示状态机已退化为流程图。

当发现问题时,应采取以下措施:

考虑将封装体分解为职责更明确的小单元,

将更多的行为转移到与有问题的封装体相关联的数据类中。

将更多的行为转移到封装体类函数中。

制作更有意义的信号,以避免对数据的依赖。

使用抽象状态机进行设计

抽象状态机是需要添加更多细节才能实际使用的状态机。抽象状态机可用于定义可复用的一般行为,这些行为将在随后的模型元素中得到进一步的改进。 图 5:一个抽象状态机请考虑图 5 中的抽象状态机。例如,图 5 中的简单状态机代表了实时系统中许多不同元素类型的最抽象的行为(自动“控制”)。尽管不同的元素类型都具有这一最高抽象程度,但是,根据其目的,它们可能在“Running”状态中具有非常不同的详细行为。因此,该状态机最有可能在某个抽象封装体类中定义,该封装体类被用作不同的专用封装体类的根类。

因此,我们将使用继承来定义该抽象状态机的两种不同的改进形式。图 6 显示了这两种改进形式 R1 和 R2。为清晰起见,我们使用浅灰笔来描绘从父类继承而来的元素。

这两种改进形式的明显差异在于它们分解“Running”(正在运行)状态的方式,以及它们扩展原“start”(开始)转移的方式。当然,只有当改进形式已知,因而未在抽象类中使用单个端到端转移来实施时,才能作出这些选择。

链式状态[编辑]

对于上述的改进类型而言,能够同时“继续”输入转移和输出转移是基本的能力。结合使用进入点、终止状态和继续转移似乎就足以提供这些语义。但是,如果有多个不同的转移需要扩展,这就行不通了。

在这种情况下,抽象行为模式需要的是用一种方法把在单个运行至结束的步骤中全部执行的两个或更多个转移段链接起来。这意味着,要将进入分层结构状态的转移拆分成一个在状态边界处有效终止的进入部分,以及一个在状态内继续的扩展部分。同样,将分层嵌套的状态所发出的输出转移分为一个在包含状态边界处终止的部分,以及一个从状态边界继续到目标状态的部分。通过引入链式状态的概念,可以在 UML 中获得这一效果。它通过 UML 状态 图6:图5 中状态机的两种改进形式概念的原型 («chainState») 来建模。该状态的唯一目的是将更多的自动(无触发器)转移“链接”到输入转移上。链式状态没有内部结构:没有进入操作,没有内部活动,没有退出操作。它也没有由事件触发的转移。但它可以有任意数量的输入转移。链式状态可能有不带触发事件的输出转移;当输入转移激活链式状态时,该转移将自动触发。这种状态的目的是将输入转移链接到独立的输出转移上。在输入转移和被链接的输出转移之间,一个状态连接到包含状态内部的另一个状态,而该状态又连接到包含状态外部的另一个状态。引入链式状态的目的是将包含状态的内部规约与其外部环境分隔开,这也是一种封装。

实际上,链式状态代表的是一种“串通”状态,它用于将某个转移链接到一个特定的继续转移。如果没有定义继续转移,转移就会在链式状态中终止。要使操作继续,就必须触发包含状态中的某一转移。

图 7 中的示例状态机段显示了链式状态及其符号。链式状态在状态机图中表示为在适当分层结构状态内的白色小圆(该符号类似于与 图 7. 链式状态和被链接的转移它们相似的初始状态和终止状态)。圆是链式状态原型的原型图标,为了方便,通常把它们描绘在边界附近。(实际上,另一种标志法是把它们描绘在包含状态的边界上,类似于封装体上的端口符号。)

该示例中,被链接的转移包括三个被链接的转移段 (e1/a11-/a12-/a13)。当收到信号 e1 时,将调用标记为 e1/a11 的转移,执行它的操作 a11,然后进入链式状态 c1。接着,调用 c1 和 c2 之间的继续转移。最后,由于 c2 也是链式状态,所以从 c2 转移到 S21。如果沿这些路径的状态都有退出和进入操作,那么实际的操作执行顺序将是:

S11 的退出操作

操作 a11

S1 的退出操作

操作 a12

S2 的进入操作

操作 a13

S21 的进入操作

以上所有操作都将在单个运行至结束的步骤中执行。

应将这些操作与直接转移 e2/a2 的操作执行语义进行对比,后者是:

S11 的退出操作

S1 的退出操作

操作 a2

状态 S2 的进入操作

状态 S21 的进入操作 © 1987 - 2001RationalSoftware Corporation。版权所有。

通用准则[编辑]

当行为的改变和状态有关时才创建状态图

敏捷建模(AM) ( Ambler 2002)的原则--最大化项目干系人的投资--建议你只有当模型能够提供正面价值的时候才创建模型。 如果一个实体,比如一个类或组件,表示的行为的顺序和当前的状态无关,那么画一个UML状态图可能是没有什么用处的。例如一个SurfaceAddrESs类就很简单,表示了那些你将会在系统中显示和操作的数据,因此一个UML状态图就没有任何相关之处。而一个Seminar对象就非常的复杂,学生注册这样一个事件将会根据它的当前状态有不同的反应,就像你在图1中看到的。

图⒈班级注册的一个UML状态图。把初始状态放置在左上角。

如你在图1所见的,初始状态被建模成一个实心圈,把初始状态放在左上角反映西方人的阅读文化的习惯。

把最终状态放置在右下角。

如你在图1所见,最终状态被建模为一个带边界的实心圆。把最终状态放右下角反映了西方的文化的从左到右,从上到下的阅读习惯。

状态指南[编辑]

状态是一个实体的行为模式的某个阶段。 状态的表示是通过实体的属性值。 例如,在图1中,当seminar被标记为open,并且存在空位的时候,seminar就处于Open For Enrollment的状态。

状态名称要简单但应具有描述性。

象Open For Enrollment和PropOSed这种的状态名称很容易理解,从而提高了图⒈的沟通价值。理论上状态名称应该是现在时,但是用过去式写成的诸如Proposed的名称要比用现在时写成的诸如Is Proposed的名称好的多。

避免"黑洞"状态。

黑洞状态是那种只有变换进来但没有任何变换发出的状态,这种情况要么由于该状态是一个最终状态,要么就是你已经错过了一个或多个变换变换。

避免"奇迹"状态。

奇迹状态是那种只有变换发出但没有任何变换进来的状态,这种情况要么由于该状态是一个起点,要么就是你已经错过了一个或多个变换变换。



链接[编辑]

Wikipedia https://en.wikipedia.org/wiki/state_diagram
Zhishi.me http://zhishi.me/baidubaike/resource/状态图
http://zhishi.me/hudongbaike/resource/状态图
http://zhishi.me/zhwiki/resource/状态图