当前位置:网站首页>UML系列文章(22)高级行为---状态机

UML系列文章(22)高级行为---状态机

2022-06-09 06:26:00 CoderIsArt

本章内容:

  • 状态、转移和活动
  • 为对象的生命周期建模
  • 创建结构良好的算法

使用交互,可以对共同工作的对象群体的行为建模。使用状态机,可以对单个对象的行为建模。状态机是一个行为,它说明对象在它的生命周期中响应事件所经历的状态序列以及对那些事件的响应。

状态机用于对系统的动态方面建模。在大多数情况下,这包括描述一个类、一个用况或整个系统实例的生命期。这些实例可能响应诸如信号、操作或计时这样的事件。当事件发生时,某些效应将依赖对象的当前状态而发生。效应(effect)是对状态机中的行为执行的规约。效应最后将细化为某些引起对象状态改变或值的返回的动作的执行。对象的状态(state)是指对象满足某些条件,执行某些活动或等待某些事件的一段时间。

可以用两种方式来可视化执行的状态:一种是强调从活动到活动的控制流(使用活动图),另一种是强调对象潜在的状态之间的转移(使用状态图)。

结构良好的状态机就像结构良好的算法一样:简单、高效、易适应而且易理解。

1.入门

在UML中,使用像类图和对象图这样的元素对系统的静态方面建模。这些图允许对系统中的事物(包括类、接口、构件、结点、用况以及它们的实例)以及它们之间的关系进行可视化、详述、构造和文档化。

    在UML中,使用状态机对系统的动态方面建模。交互是共同完成某些动作的对象群体建模,而状态机是对单个对象的生命周期建模,不管它是一个类、用况还是整个系统的实例。在一个对象的生命周期中,可能出现各种各样的事件,如信号、操作的调用、对象的创建和撤销、时间的推移或某些条件的改变。在响应这些事件时,对象通过通过某些动作(某种计算)做出反应,这些动作导致对象状态改变。因此,这样一个对象的行为是受过去影响的,至少当过去的情况在当前状态中有所反映时是这样。一个对象可以接收一个事件,并通过一个动作来响应,然后改变它的状态。一个对象还可以接收另一个事件,根据响应前一个事件产生的当前状态而做出不同的响应。

    可以使用状态机来对任何建模元素(通常是类、用况、或是整个系统)的行为建模。状态机可以用状态图来可视化。可以关注由事件引发的对象行为,这对反应式系统建模很有用。

    UML提供了对状态、转移、事件以及效应的图形表示,如下图(温控系统的状态图)所示。这种表示法允许以一种强调对象生命周期中的重要元素的方式来可视化该对象的行为。

2.术语和概念

    状态机(state Machine)是一种行为,它说明对象在它的生命周期中响应事件所经历的状态序列以及它们对那些事件的响应。状态(state)是指对象的生命周期中的条件或状况,在此期间对象将满足某些条件、执行某些活动或等待某些事件。事件(event)是对一个在时间和空间上占有一定位置的有意义的发生的规约。在状态机的语境中,一个事件是一个激励的发生,它能够触发一个状态转移。一个转移(transition)是两个状态之间的一种关系,它指明对象在第一个状态中执行一定的动作,并当特定事件发生或特定的条件满足时进入第二个状态。活动(activity)是状态机中进行的非原子执行。动作(action)是一个可执行的原子计算,它引起模型状态改变或值的返回。在图形上,状态用一个圆角的矩形表示,转移用一条从原状态指向新状态的有向实线表示。

2.1 语境

    每个对象都有一个生命期。创建时,一个对象诞生;撤销时一个对象终止其存在。在两者之间,一个对象可以作用于其他的对象(或通过做为一个消息的目标)被作用。在许多情况下,这些消息将是简单的、同步的操作调用。例如,类Customer的一个实例可能调用在类BankAccount上的一个实例上的操作getAccountBalance。像这样的对象不需要一个状态机来说明他们的行为,因为他们当前的行为并不依赖他们的过去。

    在其他种类的系统中,可能会遇到必须响应信号的对象,这些信号是在实例之间进行通信的异步消息。例如,一个移动电话必须响应随机的呼叫(来自其他电话)、按键事件(来自客户,开始一次电话呼叫)以及来自网络的事件(当电话从一个呼叫转到另一个呼叫时)。类似地,还会遇到当前行为依赖过去行为的对象。例如,一个空对空导弹制导系统的行为依赖于它的当前状态。如NotFlying(当装载导弹的飞机还停在地上时,发射导弹并不是一个好主意)或Searching(在确定攻击目标之前,不需要装备导弹)。

    用状态机能最好地说明对象的行为必须响应异步消息,或者它的当前行为依赖于过去。这包括能够接收信号的类的实例,其中包括许多主动对象。实际上,一个对象接受了信号,但是在当前状态下没有为这个信号转移,而又不延迟该信号,那么它将简单地忽略这个信号。换句话说,没有为一个信号转移并不是错误,它只是意味着在那个点对该信号不感兴趣。也可以使用状态机对整个系统的行为建模,尤其是对反应式系统,此类系统必须对来自系统外的参与者的信号做出响应。

需要说明的有两点:

1)在多数时间,将用交互图对用况的行为建模,但是也可以为了同样的用途而使用状态机。

2)类似地,可以用状态机来对接口的行为建模。尽管接口没有任何直接的实例,但实现该接口的类可以有实例。这样一个类必须遵从该接口的状态机所说明的行为。

2.2 状态

    状态是对象的生命期中的一个条件或状况,在此期间对象将满足某些条件、执行某些活动或等待某些事件。对象在一个状态下逗留有限的时间。例如,房间的加热器(Heater)可能处于如下4种状态中的任何一个:空闲(Idle)----等待开始加热房间的命令;启动(Activating)----热气打开,但等待达到某个温度;活动(Active)----热气和鼓风机都打开;关闭(ShuttingDown)----热气关闭,但鼓风机打开,吹散系统的剩余热量。

    当一个对象的状态机处于一个给定的状态时,就说这个对象处于这个状态。例如,Heater的一个实例可能处于Idle,或可能处于ShuttingDown状态。

    一个状态有以下几个部分:

1)名称(name)。一个将本状态与其他状态区分开的文本串;状态可以是匿名的,即没有名称。

2)进入、退出效应(entry/exit effect)。分别进入和退出该状态时所执行的动作。

3)内部转移(internal transition)。不导致状态改变的转移。

4)子状态(substate)。状态的嵌套结构,包括非正交(顺序活动)或正交(并发活动)子状态。

5)延迟事件(deferred event)。指在该状态下暂不处理,将推迟到该对象的另一个状态下排队处理的事件列表。

用一个圆角矩形表示一个状态。

初态和终态

    如图所示,在对象的状态机中有两个可能要定义的特殊状态。第一个是初态,表示状态机或子状态的默认开始位置。初态用一个实心的圆表示。第二个是终态,表示该状态机或外围状态的执行已经完成。终态用一个内部含有一个实心圆的圆圈表示(牛眼睛)。

2.3 转移

    转移是两个状态之间的一种关系,表示对象在某个特定事件发生而特定的条件满足时将在第一个状态中执行一定的动作,并进入第二个状态。当状态发生这样的转变时,转移被称作激活了。在转移激活之前,称对象处于源状态;激活后,则称对象处于目标状态。例如,当tooCold(带有参数desiredTemp)这样的事件发生时,Heater可能从Idle状态转移到Activating状态。 

一个转移由5部分组成:

1)源状态(source state)

源状态即受转移影响的状态,如果一个对象处于源状态,当该对象接受到转移的事件而且监护条件(如果有)满足时,将激活一个离出的转移。

2)事件触发器(event trigger)是一个事件,源状态的对象识别了这个事件,则在监护条件满足条件下激活转移。

3)监护条件(guard condition)是一个布尔表达式,当因事件触发器的接收而触发转移时,对这个布尔表达式求值:若表达式取值为真则激活转移;若为假则不激活,此时若没有其他额转移能被这个事件触发,则该事件丢失。

4)效应(effect)是一个可执行的行为(比如动作),它可以直接作用于拥有状态机的对象,并间接作用于对该对象是可见的其他对象。

5)目标状态(target state)即在转移完成后的活动状态。

如下图所示,一个转移用一条从源状态到目标状态的有向实线表示。自身转移是指源状态和目标状态相同的转移。

注解:一个转移可能有多个源(在这种情况下,它表示来自多个并发状态的一个汇合),也可能有多个目标(在这个情况下,它表示发往多个并发状态的一个分岔)。

  • 事件触发器

事件是对在时间和空间上占有一定位置的有意义的发生的规约。在状态机的语境中,事件能够触发状态转移的激励的发生。如图所示,事件可以包括信号、调用、时间推移或状态的改变。信号或调用可以带有参数,参数值对转移(包括监护条件和动作的表达式)是可见的。

    也可能有完成转移,它由一个没有事件触发器的转移表示。完成转移在源状态完成其行为(如果有)时被隐式触发。

注解:

事件触发器可能是多态的。例如,如果定义了一个信号族,那么一个触发事件是S的转移就能被S触发,也能被S任何子类触发。

  • 监护条件   

监护条件由一个方括号括起来的布尔表达式表示,放在触发器事件的后面。监护条件只在引起转移的触发器事件发生后才被计算。只要监护条件不重叠,就可能存在来自同一个源状态的、带有相同事件触发器的多个转移。

    对于每个转移,一个监护条件只在事件发生时被计算一次,但如果该转移被重新触发,则监护条件会被再次计算。在布尔表达式中可以包含关于对象状态的条件(例如,表达式是aHeater in Idle,如果对象Heater当前处于Idle状态,则它的值为真)。如果测算的时候条件不成立,那么以后条件成立的时候该事件也不会发生了。用变化事件对这种行为建模。

  • 效应

    效应是在转移激活时所执行的行为。效应包括在线计算、操作调用(调用拥有状态机的对象或其他可见的对象)、另一个对象的创建或撤销,或者向一个对象发送信号。为了表明发送一个信号,可以在信号名前加一个关键字send作为可视化提示。

    转移只发生在状态机静止时,即在它不再执行来自前一个转移的效应时。一个转移的效应以及任何相关的进入和退出效应都必须执行完毕,才允许另外的事件引发新的转移。相比之下,do活动是可以被事件中断的。

2.4 高级状态和转移

    在UML中,可以只使用状态和转移的基本特征来对广泛的、各种各样的行为建模。使用这些特征,最终可以产生简单状态机,它意味着行为模型中除了弧(转移)和顶点(状态)之外不包括其他东西。

    然而,UML的状态机具有许多可以帮助管理复杂行为模型的高级特征。这些特征常常可以减少需要的状态和转移的数量,并且将使用简单状态机时遇到的许多通用的而且有点复杂的惯用法集中在一起。这些高级特征包括进入效应、退出效应、内部转移、do活动和延迟事件。这些特征作为一个文本串显示在状态符号的文本分栏内,如下图

1)进入效应和退出效应

   在许多建模情况下,每当进入一个状态时,不管是什么转移使你进入,都想执行某个设置动作;同样,当离开一个状态时,不管是什么转移使你离开,也都想执行某个清理动作。

例如,在一个制导系统中,可能想:每当它进入Tracking状态时,显示地声明系统是onTrack的;每当它离开该状态时,声明系统是offTrack的。使用简单状态机,可以把那些动作放在每个相应的进入和退出的转移上来达到这种效应。然而,这样会有出错的危险,不得不在每次添加一个新的转移时记住增加这些动作。而且,修改这个动作意味着不得不触及每个相邻的转移。

    UML为这种惯用法提供了简洁的表示。在状态符号中包括一个进入效应(关键字entry标记)和一个退出效应(关键字exit标记),各自带有一个适当的动作。每当进入该状态时,就执行它的进入动作;每当离开该状态时,就执行它的退出动作。

   进入效应和退出效应不可以有参数或监护条件。然而,位于一个类的状态机顶层的进入效应可以有参数,用来表示当创建该对象时状态机接收到的参数。

2)内部转移

    一旦处于一个状态内,将遇到想在不离开该状态的情况下处理的事件,这就称为内部转移,它与自身转移有一些细微的不同。在自身转移中,正如上图所示,事件触发了转移后,就离开了这个状态,一个动作(如果有)被执行,然后又重新进入同一个状态。因为这个转移先退出并且随后又进入该状态,所以,自身转移先执行该状态的退出动作,接着执行自身转移动作,最后执行该状态的进入动作。

     假设想处理这个事件,但并不想执行该状态的进入和退出动作,UML用内部转移为这种用法提供了一个便捷方式。内部转移(internal transition)是这样一种转移:它通过执行一个效应来响应事件,但不改变状态。在上图中,事件NewTarget标识了一个内部转移:如果在对象处于Tracking状态时该事件发生,则执行动作Tracker.acquire,但状态维持不变,而且不执行进入或退出动作。内部转移不用转移箭头表示,而是表示为状态符号内部的一个转移串(包括事件名字,可选的监护条件和效应)。要注意的是,关键字entry、exit、和Do都是保留字,不能用作事件的名字。如果对象处于某个状态,而且该状态的某个内部转移事件发生了,那么就执行相应的效应,而不用离开和再进入该状态。因此,处理这样的事件不用调用状态的退出和进入动作。

注解:内部转移可以有带参数和监护条件的事件。

3)do活动

    当对象处于一个状态时,它一般是空闲的,在等待着一个事件的发生。但是在某些时候,可能希望对一个持续的活动建模。在处于一个状态的同时,对象做着某些工作,并一直继续直到被一个事件所中断。例如,若一个对象处于Tracking状态,只要它在该状态中,它就执行followTarget。在UML中用特殊的do转移来描述执行了进入动作后在一个状态内部所做的工作。也可以说明一个行为,如动作序列-----do/op1(a); op2(b); op3(c);。如果某个事件的发生导致了一个离开当前状态的转移,那么当前状态任何正在进行的do活动将会立刻停止。

4)延迟事件

    在各种建模情况下,可能想识别一些事件,而忽略另一些事件。识别那些作为转移的事件触发器的事件,忽略那些直接扔掉的事件。然而在某些建模情况下,可能想接收某些事件,但延迟到以后对其响应。例如,当处于Tracking状态时,可能要想延迟响应selfTest这样的信号,它们或许是由系统中的某些维护代理发送的。

    在UML中,可以用延迟事件来描述这种行为。延迟事件是其处理在该状态中被延迟的一个事件,直到另一个状态被激活才对其进行处理。如果这个事件在那个状态中没有被延迟,那么这个事件就会像刚发生一样的被处理而且可能触发转移。如果状态机通过了一系列的延迟该事件的状态,那么事件会保持到某个不会延迟该事件的状态出现为止。

注解:延迟事件的实现需要有一个内部事件队列。如果一个事件发生,并被列为延迟事件,则进入队列。一旦对象进入一个不延迟这些事件的状态,这些事件就会从这个队列中移除。

5)子状态机

在一个状态机中可以引用另一个状态机。被引用的状态机被称为子状态机(substatemachine)

这在建立结构化的大型状态模型时是有用的。

2.5 子状态

    状态与转移的这些高级特征解决了许多常见的状态机建模问题。然而,UML状态机还有另一个特征,即子状态,它能帮助简化对复杂行为的建模。子状态是嵌套在另一个状态中的状态。例如,一个加热器Heater可能处于加热Heating状态,且在Heating状态中还有一个嵌套状态Activating。在这种情况下,应该称这个对象既处于Heating状态,又处于Activating状态。

     简单的状态是没有子结构的状态。一个含有子状态(即嵌套状态)的状态被称为组合状态。组合状态包括并发(正交的)子状态或顺序(非正交)子状态。在UML中,表示组合状态就像表示一个简单的状态一样,但还要用一个可选的图形分栏来显示一个嵌套状态机。子状态可以嵌套到任何层次。

1)非正交子状态

 

2)历史状态

 

3)正交子状态

 

4)分岔和汇合

 

5)主动对象

    对并发建模的另一种方式使用主动对象。因此,不是把一个对象的状态机划分成两个或多个并发区域,而是定义两个主动对象,每个负责一个并发区域的行为。如果这些并发控制流中的一个控制流行为受到其他控制流状态的影响,就用正交区域来建模。如果这些并发流中的一个控制流的行为受到与其他控制流来往的消息的影响,就会主动对象来建模。如果并发流之间的通信很少或根本就没有,通常主动对象建模会使设计决策更明显。

3.常用建模技术

使用状态机通常目的是为对象的生命期建模。交互对一起工作的对象群体行为建模,而状态机对单个对象的整个生命期的行为建模。主要描述以下三种事物:对象能够响应的事件、这些事件的响应以及过去对当前行为的影响。

为对象的生命期建模时,要遵循如下策略:

  • 设置状态机的语境
  • 建立这个对象的初态和终态。
  • 决定这个可能响应的事件。
  • 从初态开始到终态,列出这个对象可能处于的顶层状态。
  • 识别任何进入动作和退出动作。
  • 如果需要,通过子状态来扩充这些状态。
  • 通过跟踪状态机,根据期望的事件顺序及其响应进行检查。努力寻找那些不可达状态和导致机器停止的状态。

下图显示了安全系统控制器的状态机

    在这个控制器类的生命期中有4个主要的状态:“”“初始化”Initializing(控制器开始运行)、“空闲”Idle(控制器准备好,并等待警报或来自用户的命令) 、“命令”Command(控制器正在处理来自用户的命令)和“活动”Active(控制器正在处理一个警报条件)。

    当第一次创建这个控制器对象时,首先进入Initializing状态,然后无条件地进入Idle状态。这两个状态的详细信息并不显示,但要显示Idle状态中带有时间事件的自转移。这种时间事件在嵌入式系统中是常见的,它常常有一个心跳定时器,每隔一段时间就检测一下系统的健康状况。

    当接收到一个警报alarm事件时,控制从Idle状态转移到Active状态。

进入Active状态时,setAlarm作为进入动作执行,控制首先传送到Checking状态(验证这个警报),然后传送到Calling状态(呼叫警报公司以登记这个警报),最后传送到Waiting状态。仅当“清除”clearing警报时,或是用户向控制器发“注意”attention信号以通知可能发布一个命令是,才退出状态Active和Waiting.

    注意这里没有终态。在嵌入式系统中也是常见的,希望系统无限期运行。

4.提示与技巧

一个结构良好的状态机,应满足如下要求:

  • 简单
  • 清晰的语境
  • 有效
  • 可理解
  • 不要太深嵌套
  • 有节制地使用正交子状态(主动类常常是更好的选择)

绘制UML状态机时,要遵循如下策略:

  • 避免交叉的转移线
  • 只有对于理解图形是必须时才扩充组合状态。
原网站

版权声明
本文为[CoderIsArt]所创,转载请带上原文链接,感谢
https://blog.csdn.net/zkmrobot/article/details/125107580