在面向对象的分析与设计方法体系中,统一建模语言为开发人员提供了一整套图形化表达工具,用于从不同视角刻画软件系统的结构与行为。软考考生在复习这一模块时,往往能熟练区分用例图、类图、对象图、组件图、部署图这些静态结构图,也大致知道顺序图、活动图、协作图属于行为建模范畴,但一旦具体问到某一种图的适用边界和核心语义,就容易张冠李戴。本文聚焦的UML状态图,正是行为建模工具中语义最严谨、也最容易在细节上丢分的一种。
按照官方规范的表述,状态图用于描述一个对象在它的生命周期内,因外部事件的触发而在不同状态之间进行迁移的动态行为过程。这里有两个关键词需要先锚定:其一是"单个对象",状态图的观察对象始终是一个具体实例,例如一台打印机、一笔订单、一个账户;其二是"状态迁移",状态图刻画的是对象在受到刺激后从一种状态切换到另一种状态的规则。抓住这两点,就能从根子上区分状态图与活动图:活动图强调"做什么"的流程流转,控制流可跨越多个对象;状态图强调"对象处于什么状态"的变迁,全程围绕同一对象展开。
在UML 2.x版本的术语体系中,状态图被正式更名为状态机图。这一改名并非单纯措辞调整,而是为了突出其理论基础——David Harel在1987年提出的状态图理论。Harel状态图在传统有限状态机的基础上引入了层次化、并发、历史等扩展机制,使其能以更紧凑的方式描述复杂系统的状态逻辑。软考命题中经常出现"状态图"与"状态机图"两种称呼,考生应当明确二者指向同一概念,只是版本表述不同。
从知识体系的定位看,状态图与用例图、类图、顺序图构成了面向对象建模的一组完整视角:用例图回答"系统要做什么",类图回答"系统由哪些对象构成",顺序图回答"对象之间如何按时间顺序交互",而状态图回答"单个对象自身如何随时间演化"。这四个视角互为补充,缺一不可。在软考的案例分析中,命题人常要求考生根据题干判断"应当使用哪一种UML图对系统的某个方面建模",此时是否抓住"单对象生命周期"这一线索,往往是得分与失分的分水岭。
要真正理解状态图,必须先纠正一个常见的直觉误区:状态不等于"静止不动"。在状态机语义中,状态描述的是对象满足某一组条件、并且在这些条件持续成立的期间内所处的相对稳定的局面。一个对象进入某个状态,意味着它的某个属性或属性组合进入了某个特定取值区间,并且只要对象没有收到会改变这一取值的刺激,它就会一直停留在这个状态下。因此,状态的核心不在于对象"没在做事",而在于对象的属性条件"正在被满足"。例如,一笔订单处于"待支付"状态,意味着它的支付状态属性取值为"未支付"且订单尚未取消,这一条件被满足的整个时间区间,都归属于"待支付"这一个状态。
从图例上看,状态图用一个圆角矩形表示一个状态,矩形内部书写状态名称,展开形式下还可划分多个隔间,分别记录进入动作、退出动作、内部活动等内容。
一个状态通常携带三类与动作相关的内容:进入动作,即对象刚进入该状态那一刻执行的动作;退出动作,即对象离开状态、进入下一状态之前执行的动作;内部活动,即对象处于该状态期间持续执行的动作。进入动作只在切换瞬间执行一次,内部活动在整个状态存续期间持续进行,退出动作在即将离开状态的瞬间执行。三者触发时机截然不同,是命题人区分考生是否真正理解状态机语义的关键考点。
如果说状态是状态图的"节点",那么转换就是连接这些节点的"边"。一条完整的转换由三个要素构成:事件触发器、监护条件与动作。事件触发器是引起状态迁移的刺激来源,它指明"什么情况下对象应该考虑离开当前状态";监护条件是一个布尔表达式,它在事件发生的那一刻被求值,用于判断"这个刺激在当前条件下是否真的允许对象迁移";动作则是当迁移真正发生时对象所执行的附加行为。三者分工明确:事件负责触发,条件负责把关,动作负责执行。
以电风扇为例,风扇处于"关闭"状态时,按下开关事件到来,若监护条件"风扇正常"为真,则发生从"关闭"到"转动"的转换,并执行"启动电机"动作;若风扇已损坏,则即使事件到来,条件求值为假,转换不发生。这个例子揭示了一个重要原理:事件到来只是转换的必要条件而非充分条件,监护条件的真假才是最终决定因素。许多考生失分,正是误以为"事件发生就一定会导致状态改变"。
转换在图形上用一条带箭头的实线表示,从源状态指向目标状态。当对象处于源状态,且标定在转换上的事件发生、监护条件为真时,对象就会离开源状态、执行转换上的动作、进入目标状态。这一整套流程是一个原子的、不可分割的过程,对象在任何时刻都不会处于"既在源状态又已经触发转换"的中间态。理解了转换的原子性,才能正确回答关于状态机执行顺序的题目。
在状态机的执行语义中,事件触发器与监护条件的判定时机存在一个微妙的先后关系,这个先后关系正是软考选择题反复考察的难点。规范明确要求:只有当事件发生时,系统才会去检查转换上关联的监护条件。换言之,监护条件的求值不是持续进行的,而是被事件"唤醒"的。如果事件没有发生,即使监护条件实际上已经变为真,对象也不会主动发生迁移。
这一规则可以概括为"事件驱动、条件把关"。命题人在出题时,经常通过改变叙述的措辞来迷惑考生,例如把"监护条件只有在对应事件发生时才进行检查"改成"监护条件在对象处于该状态的任何时候都会被持续检查",后者就是典型的错误表述。考生如果理解了事件与条件之间的触发关系,就能一眼识破这类偷换概念的手法。当转换上没有标注监护条件时,等价于该条件恒为真,只要事件到来,对象就会离开当前状态。
这里还要辨析"事件""动作""活动"三者的区别。事件是对象外部或内部发生的瞬时现象,不占时间;动作是对象执行的瞬时操作;活动是对象在一段时间内持续执行的行为。动作可以附着在转换上,也可附着在状态的进入、退出时机上;活动只能附着在状态内部。这一区分是判断"活动能否在转换时执行"这类叙述正误的理论基础,考生务必牢记:活动只能在状态内执行,动作既可在状态内也可在转换时执行。
当一个对象的状态逻辑变得复杂时,平面的状态图会迅速膨胀,难以阅读和维护。为此,Harel状态图引入了层次化的嵌套状态机制。所谓嵌套状态,是指一个状态内部可以包含多个子状态,这些子状态共同构成一个复合状态。对象处于复合状态时,必然同时处于其中某一个子状态,这个被激活的子状态称为活动子状态。复合状态的引入,使得多个子状态可以共享同一组进入动作、退出动作和外部转换,从而避免了在图上一遍遍重复绘制相同的转换箭头。
嵌套状态最典型的例子是交通信号灯的控制逻辑。一个交通灯处于"运行"这个复合状态时,其内部又包含"红灯""绿灯""黄灯"三个子状态,对象在"运行"期间必然处于其中某一个子状态。当外部发生"关闭信号灯"的事件时,无论当前处于哪个子状态,对象都会从"运行"复合状态整体退出,切换到"关闭"状态。这种"从复合状态整体退出"的语义,是嵌套状态带来的重要简化能力,也是案例分析与选择题中的高频考点。
与嵌套状态的层次化相对应,Harel状态图还提供了正交区域机制来表达并发。当一个对象的不同行为维度可以同时进行时,可以用一条虚线把一个复合状态划分为多个正交区域,每个区域内部各自维护一个独立的活动子状态。对象处于该复合状态时,会同时处于每个区域的一个子状态中。例如,一个正在行驶的汽车,其状态可以同时从"档位"和"照明"两个维度来描述,档位维度处于"前进"子状态的同时,照明维度可能处于"近光灯"子状态。这两个区域的子状态演进互不影响,共同刻画了对象的并发行为。正交区域是状态图与顺序图在表达能力上的重要分界,顺序图擅长表达对象之间的交互顺序,而状态图的正交区域擅长表达单个对象内部多个维度的并发演进。
在层次化状态机的设计中,存在一个实际而棘手的问题:当对象从复合状态退出、处理完一个临时事件之后重新返回该复合状态时,是应该回到它离开前所在的那个子状态,还是回到复合状态的默认初始子状态?为了灵活处理这两种需求,状态图引入了历史状态机制。
历史状态用一个圆圈内嵌字母H的符号表示,它记录了复合状态在退出前的活动子状态,使对象重新进入该复合状态时能够"记住"上次的位置。历史状态又细分为浅历史状态与深历史状态两种:浅历史状态只记住复合状态直接子状态的最近活动状态,即只
本篇完!