UML参考手册的详细介绍

UML参考手册的详细介绍

UML参考手册  

  第二部分 基 本 概 念  

第6章 状态机视图

6.1 概述
状态机视图通过对类对象的生存周期建立模型来描述对象随时间变化的动态行为。每一个对象都被看作是通过对事件进行探测并做出回应来与外界其他部分通信的独立的实体。事件表示对象可以探测到的事物的一种运动变化—如接受到从一个对象到另一个对象的调用或信号、某些值的改变或一个时间段的终结。任何影响对象的事物都可以是事件,真实世界所发生的事物的模型通过从外部世界到系统的信号来建造的。
状态是给定类的对象的一组属性值,这组属性值对所发生的事件具有相同性质的反应。换而言之,处于相同状态的对象对同一事件具有同样方式的反应,所以当给定状态下的多个对象当接受到相同事件时会执行相同的动作,然而处于不同状态下的对象会通过不同的动作对同一事件做出不同的反应。例如,当自动答复机处于处理事务状态或空闲状态时会对取消键做出不同的反应。
状态机用于描述类的行为,但它们也描述用例、协作和方法的动态行为。对这些对象方面而言,一个状态代表了执行中的一步。我们通常用类和对象来描述状态机,但是它也可以被其他元素所直接应用。
6.2 状态机
状态机是展示状态与状态转换的图。通常一个状态机依附于一个类,并且描述一个类的实例对接受到的事件所发生的反应。状态机也可以依附于操作、用例和协作并描述它们的执行过程。
状态机是一个类的对象所有可能的生命历程的模型。对象被孤立地从系统中抽出和考察,任何来自外部的影响被概述为事件。当对象探测到一个事件后,它依照当前的状态做出反应,反应包括执行一个动作和转换到新状态。状态机可以构造成继承转换,也能够对并发行为建立模型。
状态机是一个对象的局部视图,一个将对象与其外部世界分离开来并独立考查其行为的图。利用状态机可以精确地描述行为,但不适合综合理解系统执行操作。如果要更好地理解整个系统范围内的行为产生的影响,那么交互视图将更有用些。然而,状态机有助于理解如用户接口和设备控制器这样的控制机。
6.3 事件
事件是发生在时间和空间上的一点的值得注意的事情。它在时间上的一点发生,没有持续时间。如果某一事情的发生造成了影响,那么在状态机模型中它是一个事件。当我们使用事件这个词时,通常是指一个事件的描述符号,即对所有具有相同形式的独立发生事件的描述,就像类这个词表示所有具有相同结构的独立类一样。一个事件的具体发生叫做事件的实例。事件可能有参数来辨别每个实例,就像类用属性来辨别每个对象。对类而言,信号利用泛化关系来进行组织,以使不同的类共享公用的结构。事件可以分成明确或隐含的几种:信号事件、调用事件、修件事件、时间事件等。表6-1是几种事件类型及其描述。

6-1 事件的种类
UML参考手册的详细介绍

1. 信号事件
信号是作为两个对象之间的通信媒介的命名的实体,信号的接收是信号接受对象的一个事件。发送对象明确地创建并初始化一个信号实例并把它发送到一个或一组对象。最基本的信号是异步单路通信,发送者不会等待接收者如何处理信号而是独立地做它自己的工作。在双路通信模型中,要用到多路信号,即至少要在每个方向上有一个信号。发送者和接受者可以是同一个对象。
信号可以在类图中被声明为类元,并用关键字《signal》表示,信号的参数被声明为属性。同类元一样,信号间可以有泛化关系,信号可以是其他信号的子信号,它们继承父信号的参数,并且可以触发依赖于父信号的转换(如图6-1所示)。
UML参考手册的详细介绍
图6-1 信号的等级组织
2. 调用事件
调用事件是一个对象对调用的接收,这个对象用状态的转换而不是用固定的处理过程实现操作。对调用者来说,普通的调用(用方法实现的调用)不会被调用事件所辨别。接收者不是用方法来实现操作就是触发一个状态转换来实现这个操作。操作的参数即事件的参数。一旦调用的接收对象通过由事件触发的转换完成了对调用事件的处理或调用失败而没有进行任何状态转换,则控制返回到调用对象。不过,与普通的调用不同,调用事件的接收者会继续它自己的执行过程,与调用者处于并行状态。
3. 修改事件
修改事件是依靠特定属性值的布尔表达式所表示的条件的满足。这是等到特定条件被满足的一种声明途径,但是一定要小心使用它,因为它表示了一种具有时间持续性的并且可能是涉及全局的计算过程(是一种远距离的动作,因为被测试的值可能是远距离的)。这既有好处也有坏处,它的好处在于它将模型集中在真正的依赖关系上—一种当给定条件被满足时发生的作用—而不是集中在测试条件的机制上。缺点在于它使修改系统潜在值和最终效果的活动之间的因果关系变得模糊了。测试修改事件的代价可能很大,因为原则上修改事件是持续不断的。而实际上,又存在着避免不必要的计算的方法。修改事件应该仅用在当一个具有更明确表达形式的通信形式显得不自然时。
请注意监护条件与修改事件的区别。监护条件只是在引起转换的触发器事件触发时和事件接收者对事件进行处理时被赋值一次。如果它为假,那么转换将不会被激发,条件也不会被再赋值。而修改事件被多次赋值直到条件为真,这时转换也会被激发。
4. 时间事件
时间事件代表时间的流逝。时间事件既可以被指定为绝对形式(天数),也可以被指定为相对形式(从某一指定事件发生开始所经历的时间)。在高层模型中,时间事件可以被认为是来自整个世界的事件;在实现模型中,它们由一些特定对象的信号所引起,这些对象既可能是操作系统也可能是应用中的对象。
6.4 状态
状态描述了一个类对象生命期中的一个时间段。它可以用三种附加方式说明:在某些方面性质相似的一组对象值;一个对象等待一些事件发生时的一段时间;对象执行持续活动时的一段时间。虽然状态通常是匿名的并仅用处于该状态时对象进行的活动描述,但它也可以有名字。
在状态机中,一组状态由转换相连接。虽然转换连接着两个状态(或多个状态,如果图中含有分支和结合控制),但转换只由转换出发的状态处理。当对象处于某种状态时,它对触发状态转换的触发器事件很敏感。
状态用具有圆形拐角的矩形表示。如图6-2所示。

UML参考手册的详细介绍
图6-2 状态
6.5 转换
从状态出发的转换定义了处于此状态的对象对外界发生的事件所做出的反应。通常,定义一个转换要有引起转换的触发器事件、监护条件、转换的动作和转换的目标状态。表6-2 列出了几种转换和由转换所引起的隐含动作。

表6-2 转换的种类及隐含动作
UML参考手册的详细介绍
1. 外部转换
外部转换是一种改变活动状态的转换,它是最普通的一种转换。它用从源状态到目标状态的箭头表示,其他属性以文字串附加在箭头旁(如图6-3所示)。

UML参考手册的详细介绍
图6-3 外部转换
2. 触发器事件
触发器事件是引起转换的事件。事件可以有参数,以供转换的动作使用。如果一个信号有后代,那么信号中的任一个后代都可以引起转换。例如,如果转换将Mouse Button 作为触发器,那么Mouse Button Down可以触发这个转换(如图6-1)。
事件并不是持续发生的,它只在时间的一点上发生。当一个对象接收到一个事件时,如果它没有空闲时间来处理事件,就将事件保存起来。对象一次只处理一个事件,在对象处理事件时转换必须激发,事件过后是不会被记住的(某些特殊的延迟事件除外,在触发一个转换前或处延迟被解除前,这类事件被保存起来)。如果两个事件同时发生,它们被每次处理一个。没有触发任何转换的事件被简单地忽略或遗弃,这并不是一个错误,忽略不想要的事件要比详细指明所有事件容易得多。
3. 监护条件
转换可能具有一个监护条件,监护条件是一个布尔表达式。监护条件可以引用对象的属性值和触发事件的参数。当一个触发器事件被触发时,监护条件被赋值。如果布尔表达式的值为“真”,那么触发事件即,使转换有效。如果布尔表达式的值为“假”,则不会引起转换。监护条件只能在触发事件发生时被赋值一次。如果在转换发生后监护条件由原来的“假”变为“真”,则因为赋值太迟而不能触发转换。
从一个状态引出的多个转换可以有同样的触发器事件,但是每个转换必须具有不同的监护条件。当其中一个监护条件满足时,触发器事件会引起相应的转换。通常,监护条件的设置要考虑到各种可能的情况以确保一个触发器事件的发生应该能够引起某些转换。如果有些情况没有考虑到,一个触发器事件没有引起任何转换,那么在状态机视图中要忽略这个事件。一个事件的发生只能同时引起一个转换(在一个控制线程中)。如果一个事件可能引起多个转换,那么其中只有一个转换有效。如果两个相互矛盾的转换同时有效,则无法确定到底发生了哪个转换。这两个转换随机地发生一个,或者由系统的实现细节决定究竟发生哪一个,但是对建模者来说,无法预料这种转换产生的后果。
4. 完成转换
没有标明触发器事件的转换是由状态中的活动的完成引起的(即完成转换)。完成转换也可以带一个监护条件,这个监护条件是在状态中的活动完成时被赋值的(而不是完成以后)。
5. 动作
当转换被引起时,它对应的动作被执行。动作是原子性的,一般是一个简短的计算处理过程,通常是一个赋值操作或算术计算。另外还有一些动作,包括给另一个对象发送消息、调用一个操作、设置返回值、创建和销毁对象,没有被定义的控制动作用外部语言来进行详细说明。动作也可以是一个动作序列,即一系列简单的动作。动作或动作序列的执行不会被同时发生的其他动作影响或终止。按照UML中的概念,动作的执行时间非常短,与外界事件所经历的时间相比是可以忽略的,因此,在动作的执行过程中不能再插入其他事件。然而,实际上任何动作的执行都要耗费一定时间,新到来的事件必须被安置在一个队列中。
整个系统可以在同一时间执行多个动作。我们说动作是原子性的,并不是说整个系统是原子性的。系统能够处理硬件的中断和多个动作的时间共享。动作在它的控制线程中是原子性的。一旦开始执行,它必须执行到底并且不能与同时处于活动状态的动作发生交互作用。但动作不能用于表达处理过程很长的事物。与系统处理外部事件所需要的反应时间相比,动作的执行过程应该很简洁,否则系统不能够做到实时响应。
一个动作可以使用触发器事件的参数和对象的属性值作为表达式的一部分。


表6-3列出了各种动作及描述。
UML参考手册的详细介绍
表6-3 动作的种类

6. 状态改变
当动作执行完毕后,转换的目标状态被激活,这时会触发出口动作或入口动作的执行。
7. 嵌套状态
状态可以被嵌套在其他的组成状态之内(看下一段)。从一个外部状态出发的转换可以应用于这个状态所有的内部嵌套状态。任何一个内部嵌套状态被激活时,转换都有可能发生。组成状态可用于表达例外和异常,因为组成状态上的转换适用于所有它所嵌套的状态,不需要每个嵌套状态显式地单独处理异常。
8. 入口和出口动作
一个跨越多个嵌套层次的转换可能会离开或进入某个状态。只要转换进入或离开某个状态,则该状态可能包含要被执行的动作。进入一个状态可能会执行一个依附于该状态的入口动作。如果转换离开初始状态,那么在转换的动作和新状态的入口动作被执行前,执行该状态的出口动作。
入口动作通常用来进行状态所需要的内部初始化。因为不能回避一个入口动作,任何状态内的动作在执行前都可以假定状态的初始化工作已经完成,不需要考虑如何进入这个状态。同样,无论何时从一个状态离开都要执行一个出口动作来进行后处理工作。当出现代表错误情况的高层转换使嵌套状态异常终止时,出口动作特别有用。出口动作可以处理这种情况以使对象的状态保持前后一致。入口动作和出口动作原则上依附于进来的和出去的转换,但是将它们声明为特殊的动作可以使状态的定义不依赖状态的转换,因此起到封装的作用。
9. 内部转换
内部转换有一个源状态但是没有目标状态。内部转换的激发规则和改变状态的转换的激发规则相同。由于内部转换没有目标状态,因此转换激发的结果不改变本状态。如果一个内部转换带有动作,它也要被执行,但是没有状态改变发生,因此也不需要执行入口和出口动作。内部转换用于对不改变状态的插入动作建立模型(如,记录发生的事件数目或建立帮助信息屏)。
尽管入口动作和出口动作的执行是由进入或离开某状态的外部转换所引起的,除了使用保留字entry 和exit 代替触发事件名称之外,入口和出口动作使用与内部转换相同的表示法。
一个自身转移会激发状态上的入口动作和出口动作的执行(从概念上来讲,自身转换从一个状态出发后又会到自身状态),因此,自身转换不等价于内部转换。图6-4 说明了入口动作、出口动作和内部转换。
UML参考手册的详细介绍
图6-4 内部转、入口动作和出口动作
6.6 组成状态
一个简单状态没有子结构,只带有一组转换和可能的入口和出口动作。组成状态是一个被分解成顺序的或并发的子状态的状态。表 6-4 列出了各种状态。

表6-4 状态的种类
UML参考手册的详细介绍

将状态分解成互斥的子状态是对状态的一种专门化处理。一个外部状态被细分成多个内部子状态,每一个子状态都继承了外部状态的转换。在某一时间只有一个子状态处于激活状态。外部状态表达了每一个内部状态都具有的条件。
进入或离开一个组成状态的转换会引起入口动作或出口动作的执行。如果有多个组成状态,跨越多个层次的转换会引起多重入口动作(最外层最先执行)和出口动作(最内层最先执行)的执行。如果转换带有动作,那么这个动作在入口动作执行后,出口动作执行前执行。
组成状态也可能在其内部具有一个初始状态。组成状态边界上的转换隐含为初始状态上的转换。一个新对象起始于它的最外层的初始状态。如果一个对象到达了它最外层状态的终止状态,那么该对象将被销毁。初始状态、终止状态、入口动作和出口动作封装了状态的定义,使状态的定义与进出状态的转换无关。
图6-5展示了一个状态的顺序分解,其中包括一个初始状态。这是售票系统的状态机模型。

UML参考手册的详细介绍
图6-5 状态机
将一个状态分解成并发的多个子状态代表相互独立的并行处理过程。当进入一个并发超状态时,控制线程的数目增加;当离开一个并发超状态时,控制线程的数目减少对于每一个状态而言,并发通常依靠不同的对象实现,但是,并发子状态还可以代表一个单独状态内部的逻辑并发关系。图6-6展示了选修一门大学课程的并发分解。
UML参考手册的详细介绍

图6-6 带有并发组成状态的状态机
UML参考手册的详细介绍

图6-7 子机器状态
通常,可以在一个状态机中使用其他状态机的一部分,这种可重用性会带来一些方便。状态机可以命名,并可以用名字引用其他一个或多个状态机。目标状态机是一个子机器,引用这个子机器的状态叫做子机器引用状态。它的含义是在需要引用的地方用一个状态机来替换原有内容。一个状态机可以包含一个活动,即可以包含一个处理过程或一个需要消耗时间才能完成的持续过程或是可以被中断的事件,而子机器不能图6-7演示了子机器的引用。
进入一个子机器引用状态的转换会激活目标子机器的初始状态。要进入其他状态的子机器,需要在子机器引用状态中安置一个或多个桩状态。桩状态用于在子机器中标识状态。