状态
状态描述了一个类对象生命期中的一个时间段。它可以用三种附加方式说明:在某些方面性质相似的一组对象值;一个对象等待一些事件发生时的一段时间;对象执行持续活动时的一段时间。虽然状态通常是匿名的并仅用处于该状态时对象进行的活动描述,但它也可以有名字。
在状态机中,一组状态由转换相连接。虽然转换连接着两个状态(或多个状态,如果图中含有分支和结合控制),但转换只由转换出发的状态处理。当对象处于某种状态时,它对触发状态转换的触发器事件很敏感。
状态用具有圆形拐角的矩形表示。如图 6-2 所示。
图 6–2 状态
转换
从状态出发的转换定义了处于此状态的对象对外界发生的事件所做出的反应。通常,定义一个转换要有引起转换的触发器事件、监护条件、转换的动作和转换的目标状态。
表 6 – 2 列出了几种转换和由转换所引起的隐含动作。
转换的种类 |
描述 |
语法 |
入口动作 |
进入某一状态时执行的动作 |
entry/action |
出口动作 |
离开某一状态时执行的动作 |
exit/action |
外部转换 |
引起状态转换或自身转换,同时执行一个具体的动作,包括引起入口动作和出口动作被执行的转换
|
e(a:T)[exp]/action |
内部转换 |
引起一个动作的执行但不引起状态的改变或不引起入口动作或出口动作的执行
|
e(a:T)[exp]/action |
表 6–2 转换的种类及隐含动作
外部转换
外部转换是一种改变活动状态的转换,它是最普通的一种转换。它用从源状态到目标状态的箭头表示,其他属性以文字串附加在箭头旁(如
图 6 – 3 所示)。
图 6–3 外部转换
2. 触发器事件
触发器事件是引起转换的事件。事件可以有参数,以供转换的动作使用。如果一个信号有后代,那么信号中的任一个后代都可以引起转换。例如,如果转换将
Mouse Button 作为触发器,那么 Mouse Button Down 可以触发这个转换(如 图 6 – 1 )。
事件并不是持续发生的,它只在时间的一点上发生。当一个对象接收到一个事件时,如果它没有空闲时间来处理事件,就将事件保存起来。对象一次只处理一个事件,在对象处理事件时转换必须激发,事件过后是不会被记住的(某些特殊的延迟事件除外,在触发一个转换前或处延迟被解除前,这类事件被保存起来)。如果两个事件同时发生,它们被每次处理一个。没有触发任何转换的事件被简单地忽略或遗弃,这并不是一个错误,忽略不想要的事件要比详细指明所有事件容易得多。
3. 监护条件
转换可能具有一个监护条件,监护条件是一个布尔表达式。监护条件可以引用对象的属性值和触发事件的参数。当一个触发器事件被触发时,监护条件被赋值。如果布尔表达式的值为“真”,那么触发事件即,使转换有效。如果布尔表达式的值为“假”,则不会引起转换。监护条件只能在触发事件发生时被赋值一次。如果在转换发生后监护条件由原来的“假”变为“真”,则因为赋值太迟而不能触发转换。
从一个状态引出的多个转换可以有同样的触发器事件,但是每个转换必须具有不同的监护条件。当其中一个监护条件满足时,触发器事件会引起相应的转换。通常,监护条件的设置要考虑到各种可能的情况以确保一个触发器事件的发生应该能够引起某些转换。如果有些情况没有考虑到,一个触发器事件没有引起任何转换,那么在状态机视图中要忽略这个事件。一个事件的发生只能同时引起一个转换(在一个控制线程中)。如果一个事件可能引起多个转换,那么其中只有一个转换有效。如果两个相互矛盾的转换同时有效,则无法确定到底发生了哪个转换。这两个转换随机地发生一个,或者由系统的实现细节决定究竟发生哪一个,但是对建模者来说,无法预料这种转换产生的后果。
4. 完成转换
没有标明触发器事件的转换是由状态中的活动的完成引起的(即完成转换)。完成转换也可以带一个监护条件,这个监护条件是在状态中的活动完成时被赋值的(而不是完成以后)。
5. 动作
当转换被引起时,它对应的动作被执行。动作是原子性的,一般是一个简短的计算处理过程,通常是一个赋值操作或算术计算。另外还有一些动作,包括给另一个对象发送消息、调用一个操作、设置返回值、创建和销毁对象,没有被定义的控制动作用外部语言来进行详细说明。动作也可以是一个动作序列,即一系列简单的动作。动作或动作序列的执行不会被同时发生的其他动作影响或终止。按照
UML 中的概念,动作的执行时间非常短,与外界事件所经历的时间相比是可以忽略的,因此,在动作的执行过程中不能再插入其他事件。然而,实际上任何动作的执行都要耗费一定时间,新到来的事件必须被安置在一个队列中。
整个系统可以在同一时间执行多个动作。我们说动作是原子性的,并不是说整个系统是原子性的。系统能够处理硬件的中断和多个动作的时间共享。动作在它的控制线程中是原子性的。一旦开始执行,它必须执行到底并且不能与同时处于活动状态的动作发生交互作用。但动作不能用于表达处理过程很长的事物。与系统处理外部事件所需要的反应时间相比,动作的执行过程应该很简洁,否则系统不能够做到实时响应。
一个动作可以使用触发器事件的参数和对象的属性值作为表达式的一部分。
表 6–3 列出了各种动作及描述。
表 6–3 动作的种类
6. 状态改变
当动作执行完毕后,转换的目标状态被激活,这时会触发出口动作或入口动作的执行。
7. 嵌套状态
状态可以被嵌套在其他的组成状态之内(看下一段)。从一个外部状态出发的转换可以应用于这个状态所有的内部嵌套状态。任何一个内部嵌套状态被激活时,转换都有可能发生。组成状态可用于表达例外和异常,因为组成状态上的转换适用于所有它所嵌套的状态,不需要每个嵌套状态显式地单独处理异常。
8. 入口和出口动作
一个跨越多个嵌套层次的转换可能会离开或进入某个状态。只要转换进入或离开某个状态,则该状态可能包含要被执行的动作。进入一个状态可能会执行一个依附于该状态的入口动作。如果转换离开初始状态,那么在转换的动作和新状态的入口动作被执行前,执行该状态的出口动作。
入口动作通常用来进行状态所需要的内部初始化。因为不能回避一个入口动作,任何状态内的动作在执行前都可以假定状态的初始化工作已经完成,不需要考虑如何进入这个状态。同样,无论何时从一个状态离开都要执行一个出口动作来进行后处理工作。当出现代表错误情况的高层转换使嵌套状态异常终止时,出口动作特别有用。出口动作可以处理这种情况以使对象的状态保持前后一致。入口动作和出口动作原则上依附于进来的和出去的转换,但是将它们声明为特殊的动作可以使状态的定义不依赖状态的转换,因此起到封装的作用。
9. 内部转换
内部转换有一个源状态但是没有目标状态。内部转换的激发规则和改变状态的转换的激发规则相同。由于内部转换没有目标状态,因此转换激发的结果不改变本状态。如果一个内部转换带有动作,它也要被执行,但是没有状态改变发生,因此也不需要执行入口和出口动作。内部转换用于对不改变状态的插入动作建立模型(如,记录发生的事件数目或建立帮助信息屏)。
尽管入口动作和出口动作的执行是由进入或离开某状态的外部转换所引起的,除了使用保留字 entry 和 exit 代替触发事件名称之外,入口和出口动作使用与内部转换相同的表示法。
一个自身转移会激发状态上的入口动作和出口动作的执行(从概念上来讲,自身转换从一个状态出发后又会到自身状态),因此,自身转换不等价于内部转换。
图 6 – 4 说明了入口动作、出口动作和内部转换。
图 6–4 内部转、入口动作和出口动作