首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
UML状态图详解:事件驱动、状态转换与建模实践
📅 2026/10/29 5:11:02
✍️ 爱科研究院
👁 阅读 3,247
简介这份PPT围绕UML状态图展开面向学习统一建模语言、准备进行系统分析与概要设计的初学者与中级开发者重点解决事件、状态、转换等核心概念的梳理与建模应用问题。压缩包内仅含1个PPT文件体积约285KB以幻灯片形式串联概念讲解、示例图示与课堂任务轻量易读便于随堂演示或自学翻阅目前已有374人学习下载。内容从信号事件、调用事件、变化事件与时间事件四类事件切入说明状态的名称、进入/退出动作、内部转换、子状态与延迟事件等组成要素并借打印机就绪、打印、缺纸等状态以及图书馆管理系统借书、还书用例演示状态图的绘制思路与转换触发条件。读者可据此掌握对象生命周期的可视化表达方法并把静态类抽象与交互图描述衔接起来用于软件架构的概要设计。1. 从洗衣机面板说起UML 状态图到底在画什么洗衣机面板上那排小灯——“洗涤”“漂洗”“脱水”“完成”——其实就是一张图。同一时刻只亮一个灯亮到哪一格由“按下启动”“水位到了”“定时结束”这几件事推着走。UML 状态图画的正是这张图它不关心一个对象长什么样、存了哪些字段只关心它一辈子会停在哪些瞬间、又是被什么推着换到下一格。口头那句“这个按钮按下去会怎样”在它这里变成一张别人也能照着核对的图。很多人第一次听到“状态图”三个字会以为它跟流程图差不多不过是换了个名字。真画起来才发现不一样流程图讲的是“事情按什么顺序做”状态图讲的是“某个东西在某一刻处于什么处境什么条件下允许它换处境”。这个差别听着小落到代码上却很实在——一个按钮在“待机”时按下去是开机在“运行”时按下去是关机同一件事在不同处境里结果完全相反流程图说不清状态图一句话就写明白了。2. 事件那只把对象从一格推到下一格的手2.1 事件是“发生了什么”不是“做了什么”事件在 UML 里的原话有点绕它表示对一个在时间和空间上占据一定位置的有意义的事情的规格说明。翻译成人话就是——一件发生了、并且值得被记一笔的事情。按下电视机上的 Power 按钮这件事叫事件电视开始播放这是事件引起的动作。事件是外界的推力动作是这个对象自己做出的反应两者一前一后不能混成一个词。地铁闸机是最好的例子。你刷卡这是事件闸机开门这是动作。闸机不会自己找上门来让你刷它只是安静地待在“等待刷卡”这个处境里直到事件把它推走。如果卡余额不足事件照样发生了闸机却选择不开门——这说明事件的到来和对象怎么反应中间还隔着一层判断这一层后面讲转换时要专门说。事件分外部和内部两种。外部事件是在系统和参与者之间传送的比如读者在借书界面上点了“确认借阅”内部事件是在系统内部的对象之间传送的比如库存对象给借阅对象发了一条“库存已扣减”。为什么要分内外因为画图时你得知道这只手是从哪伸过来的——从系统边界外来说明它对应一个用例的入口从对象之间来说明它对应一条内部的消息链路。注意事件是“规格说明”也就是对事情的描述而不是事情本身。图上一个“按下启动”描述的是“某时某地发生了一次按下启动”不是“现在请按下启动”。2.2 四种事件信号、调用、变化、时间UML 把事件细分成了好几种日常画图最常打交道的就四类它们的区别不在名字而在“谁在等谁”。信号Signal是两个对象之间通信媒介的命名实体接收方收到信号就算收到一个事件。它和普通的类长得很像可以有属性可以有实例还能参与泛化关系。计算机设备的中断信号是一般的信号键盘中断信号就是它的特殊子信号继承父信号的属性还能触发包含信号类型的转换。在类图里信号被声明成一个带Signal构造型的类信号的参数写成属性一个操作如果会发出某个信号就用构造型为send的依赖关系连过去。startuml 信号用构造型类表示类里的字段就是信号携带的数据 class Collision Signal { force : Float } class MovementAgent { position velocity moveTo() } send 依赖表示 moveTo() 这个操作会发出 Collision 信号 MovementAgent .. Collision : send enduml这段描述里Signal标明 Collision 是一个信号而不是普通类force : Float是信号随身带的数据也就是参数。send那条虚线依赖表达的是“发出”不是“拥有”——MovementAgent 并不持有 Collision 这个类只是在moveTo()执行时把它扔出去。参数说明白了这个信号一旦被接收方拿到手里就多了一个force值可供判断。调用事件Call Event是一个对象对操作调用的接收。它和信号最本质的差别是同步性信号是异步的发出去就不管了调用事件一般来说是同步的控制从发送者手里交到接收者手里接收者触发转换、做完操作、转换到新状态控制再还给发送者。图书系统里“查询图书”这件事就是典型的调用事件——未验证的图书状态收到searchBook(name)这个调用验证通过后转到已验证状态然后才把结果交回去。变化事件Change Event依赖一个布尔表达式表达式为真时才触发。写法是用关键字when后面跟条件。它可以是一个绝对时刻比如when time 10:00也可以是持续监测的条件比如when altitude 1000。打印机缺纸时转到“缺纸”状态等纸盒被装满这个条件满足才触发打印测试动作——这就是变化事件在干活。时间事件Time Event表示一段时间的推移写法是关键字after加时间表达式。它的计时基准默认是“进入当前状态的那一刻”这一点常被忽略after(2 seconds)指的是进入这个状态之后的两秒不是从系统启动算起的两秒。打印就绪状态等两秒后自动连上打印机开始打印写的就是after(2 seconds)/ connectionPrint()。事件类型触发条件同步性典型写法信号接收方收到一个信号实例异步Signal类 send依赖调用收到一次操作调用同步searchBook(name)变化布尔表达式为真由对象自己轮询/判定when (条件)时间经过指定时长由时间推移触发after (时长)2.3 同步和异步这道坎为什么不能混着写初学者最容易犯的错是把一次同步调用画成信号或者把异步信号写成调用。表面上箭头都长一样实际语义差得远。信号是“我喊一嗓子你爱什么时候处理什么时候处理”。发送方发完继续干自己的事不关心接收方是否活着、什么时候回。调用是“我把活交给你我在这儿等着”。这个区别直接决定图里要不要画“超时”和“失败返回”这两条路。把同步调用当信号画图上就只有一条笔直向前的箭头看起来皆大欢喜可真实系统里对方可能迟迟不返回、可能抛异常这些路径全被那张图吃掉了。等项目上线发现“点了确认按钮页面卡死”回头再看那张漂亮的状态图它一个字都没提过这事儿。判断方法其实很土问一句“发出去之后我还等不等结果”。等就是调用事件画完记得补上返回和超时不等就是信号接收方那边自己得有个队列接着。这个判断做对了后面状态和转换才站得住。3. 状态对象一生中愿意停下来的那几个瞬间3.1 一个状态里装了什么名称、动作、内部转换、延迟事件状态的定义是在对象的生命周期中满足某些条件、执行某些活动或等待某些事件时的一个条件或状况。这话拆开看落在三个词上——条件、活动、等待。一个对象在某个状态里要么在持续满足某个条件要么在持续做某件事要么在干等某件事发生。打印机是讲状态的好素材。它在工作时有六种状态就绪、打印、缺纸、忙、暂停、停止。这六种不是随便列的每一种都对应一段可观察的处境就绪是“等着有人发打印任务”打印是“正在吐纸”缺纸是“卡住了等人来装纸”忙是“正在处理但还没开始出纸”暂停是“人按了暂停键”停止是“关机或故障”。具体那台打印机在 UML 里是对象这六种处境就是状态图里的状态。状态内部不止一个名字它能装五样东西。名称是把这格和别的格区分开的字符串状态也可以是匿名的——匿名的意思是不写名字但位置和连线照样表达含义。进入动作和退出动作entry/exit分别是进入和离开这个状态时执行的动作注意它们是“进出的那一瞬间”做的事不是“待在里面时”做的事。内部转换是不导致状态改变的转换比如在“打印”状态里每打印一行就计数器加一。子状态是嵌套结构里的下一层分顺序活动和并发活动两种。延迟事件是在这个状态里暂时不处理、但要排队等到了另一个状态再处理的事件列表。把这五样摆到一个状态里一个“打印”状态大概是这副样子entry 动作做初始化、清缓冲区exit 动作做收尾、通知队列内部转换负责“换行/计数器加一”这种不动状态的小事延迟事件记着“用户按了暂停等这页打完再说”子状态则可能把“打印”拆成“取纸”和“喷墨”两件同时进行的事。3.2 什么该提成状态看这个值变了行为会不会跟着变初学者最常见的冲动是把对象身上每个字段都画成一个圈。图书有“书名”“作者”“ISBN”“价格”“库存数量”于是图上出现五个状态——这是错的而且错得很典型。区别标准只有一条这个值的变化会不会让对象对外界事件的态度发生变化。价格从 39 元改成 45 元图书照样能被借、被还、被查询行为一点没变那它就是属性值老老实实待在类图里。但“在架”和“借出”不一样书在架时借书请求被接受书已借出时同一个借书请求会被拒绝或者被排进预约队列。行为变了它就该是一个状态。再补一刀状态是“一段时间内持续成立的条件”属性值是“某一刻的读数”。温度计读数是 26 度这是值空调处于“制冷中”这是状态因为它会持续一段时间并且在这段时间里持续地对“调高温度”这个事件做出反应。搞混这两个图会迅速膨胀成几十个圈画的人自己都念不下来。3.3 子状态顺序的和并发的状态可以套娃。一个状态里再放几个小状态叫子状态分两种。顺序子状态也叫不相交子状态是说同一时刻只有一个子状态活着。比如“借出中”这个大状态里可以细分成“校验读者资格”“扣减库存”“生成借阅记录”三个小状态它们一个接一个任何时刻只有一个在生效。这种嵌套的价值在于把公共的进入/退出动作提到外层不管里面走到哪一步只要还在“借出中”退出时的回滚逻辑写一次就够了。并发子状态是说几个子状态同时活着。比如“打印”状态里“取纸”和“喷墨”可以同时进行纸张到位和墨路准备好各自推进两边都就绪才允许整体转出。并发画法通常是把状态框切成几块每块里一条独立的小状态线。并发这个点很容易被滥用。有人一看到“同时”两个字就想拆并发结果图上一堆并行线实际业务里这些事根本就是先后发生的。判断标准还是行为如果 A 没做完 B 就不能开始那是顺序不是并发只有当两件事真的互不等待、各自推进时才值得画成并发。3.4 延迟事件先记账回头再算延迟事件是个很实用、但极少有人用的东西。它的意思是这个事件在当前状态里不处理但也不丢弃先记在账上等对象转到另一个状态时再拿出来处理。打印机缺纸的时候用户按了“取消打印”。此时机器正在缺纸状态没法立刻执行取消但直接把这个请求扔掉又太粗暴。合理的做法是把它列进缺纸状态的延迟事件表等纸张装好、回到就绪状态这条“取消打印”再被翻出来执行。这么画出来的图行为是完整的不这么画就得在缺纸状态里额外加一条“取消”转换看起来能用实际逻辑是错的——缺纸时根本没在打印取消什么用延迟事件的前提是这个事件确实该在当前状态被推迟而不是被忽略。如果业务上就是“缺纸时按取消无效”那不该用延迟事件直接不画这条线或者画一条被丢弃的说明就行。这个区分要在图边上写清楚否则读图的人只能猜。4. 转换箭头上的文字才是信息量最大的地方4.1 一条箭头写了五样东西源、目标、事件、守卫、动作转换是两个状态之间的一种关系表示对象将在当前状态执行动作并在某个特定事件发生或某个特定条件满足时进入后继状态。转换激活之前对象处于源状态激活之后对象处于目标状态。中间那个“激活”的瞬间就是事件和条件同时成立的那一刻。一条完整的转换可以写五样东西源状态、目标状态、触发事件、守卫条件、动作。前两样由箭头的两头决定后三样写在箭头上标准写法是事件名 [守卫条件] / 动作方括号里是守卫条件斜杠后面是动作。三者都可以省略但省略得越多这条转换能表达的信息就越少。一条只写了动作、没写事件也没写守卫的箭头读图的人只能猜“这到底什么时候走”。举个具体的程序从“就绪”转到“运行”触发事件是“获取时间片”。写成获取时间片就够了因为没有额外条件。但如果还要判断优先级就得写获取时间片 [优先级 当前进程] / 切换上下文。看一条箭头的信息量全在这几个方括号和斜杠里。4.2 守卫条件和动作谁管“能不能走”、谁管“走过去干什么”这两个东西经常被写反。守卫条件是一个布尔表达式它回答的是“这条转换允许不允许发生”动作是转换真正发生时执行的行为它回答的是“走过去的时候顺手做点什么”。一个类比守卫条件是检票口动作是过闸机之后领的那张导览图。票不对闸机不开导览图也就无从谈起票对了闸机开导览图照发。所以守卫条件必须能求值成真或假不能写成一段操作动作则可以是一串操作甚至是调用别的对象。守卫条件的另一个要点是互斥性。同一个源状态出来的多条转换如果它们的事件一样、守卫条件又可能同时为真那就是一张有歧义的图——对象不知道该走哪条。好的写法是让这些条件彼此不重叠实在重叠就补一条优先级更高的兜底条件。这一点在评审时特别值得盯因为画图的人心里默认了顺序读图的人却看不到。动作还有个小细节转换上的动作是“转换发生时”执行的不是“进入目标状态后”执行的。如果你要的是后者应该写进目标状态的 entry 动作里。两者在图上位置不同执行时机也不同混着写会让实现的人做出错误的选择。4.3 借书和还书两张图具体怎么落笔拿图书业务模块的两个用例走一遍就能看出上面这些概念怎么落到纸上。借书用例盯住“图书”这个对象看它的状态变化。候选状态是在架、借出中、已借出。三条转换借书申请把书从在架推到借出中守卫条件是读者资格有效动作是登记借阅记录借出中确认出库后进入已借出动作是更新库存借出中如果申请被拒回到在架动作是回滚占位。这里有个容易漏的点借出中是一个中间态它有超时风险如果一直没确认应该有时限事件把它拉回在架。startuml 借书用例以“图书”对象为中心看它的状态变化 [*] -- 在架 在架 -- 借出中 : 借书申请 [读者资格有效] / 登记借阅记录 借出中 -- 已借出 : 确认出库 / 更新库存 借出中 -- 在架 : 申请被拒 / 回滚占位 借出中 -- 在架 : after(30 minutes) [未确认] / 释放占位 已借出 -- 在架 : 还书登记 / 核销记录 enduml图中的方括号是守卫条件斜杠后是转换动作after(30 minutes)是时间事件它的计时起点是进入“借出中”那一刻。最后一条转换是从已借出回到在架它其实属于还书用例的范畴放在同一张图里是为了让“图书”这个对象的生命周期闭合——状态图最忌讳画成一条单向直线有去无回。还书用例可以单独画一张盯住“归还处理”这条链路书已还、归还处理中、归还成功。归还处理中同样是个中间态要处理逾期判断、损坏登记、上架排队这些事任何一步失败都得有回退路径。两张图分开画是因为它们描述的虽然都是同一本书但关注的行为集合不同合在一起画不是不行只是图会变得很大读的人容易走神。提示每个中间态都问一句“如果一直没人推它会不会永远卡在这儿”。会就补一条时间事件或异常事件这是状态图从“能看”到“能用”的分水岭。4.4 别把内部转换画成自转换自转换是一条从状态出发、又回到同一个状态的箭头。内部转换是写在状态框里面的一行“事件/动作”不画箭头。两者长得像语义差得远。自转换会触发退出动作和进入动作对象离开这个状态再重新进来exit 和 entry 各跑一遍。内部转换不触发这两个动作对象根本没离开。打印状态里“每打印一行计数器加一”如果你画成自转换那每打一行都要跑一次初始化和清理性能和行为都不对写成内部转换才符合预期。判断方法问一句“这件事要不要重新走一遍进出的流程”。要画自转换不要写内部转换。这个坑很隐蔽因为图上看起来只是箭头和文字的区别跑起来才发现初始化被反复执行。5. 易混与误区画着画着就翻车的几个地方5.1 状态图和活动图都是流程图吗易混点两者都能画出“从这一步到下一步”的流动感画出来都是一堆框加箭头。为什么混状态图和活动图在视觉上确实接近尤其当状态数量少、流程又线性的时候两张图几乎可以互换着看。怎么区分看这张图的主体是谁。状态图的主体是“一个对象”它问的是“这个对象现在处于什么处境”活动图的主体是“一件事”它问的是“这件事分几步做完、谁来做的”。状态图里的框是名词性的就绪、打印、缺纸活动图里的框是动词性的提交申请、审核、归档。还有一个更硬的区别状态图的箭头必须由事件触发活动图的箭头可以只是做完了就往下走不需要外部事件推。5.2 状态和属性值是不是一回事易混点对象的每个字段看上去都能变成图上的一个圈图书有五个字段就画五个状态看起来挺整齐。为什么混字段和状态都描述对象的“当前情况”而且很多状态的名字确实和某个字段的取值重名比如“是否借出”这个布尔字段和“已借出”这个状态。怎么区分回到那条标准——值变了对象对外界事件的态度变不变。不变是属性变了是状态。另一个角度是时间性属性是某一刻的读数状态是一段时间内持续成立的条件并且在这段时间里对象会持续地对某些事件做出反应。用这条标准过滤一遍五个字段里通常只剩一两个能升级成状态图立刻清爽。5.3 事件和动作名字写反了会怎样易混点图上的箭头标签经常写成“打印”“保存”“提交”这种动词看起来像事件其实可能就是动作。为什么混中文里事件和动作都可以用动词表达“按下按钮”是事件“开始播放”是动作字面上都是动宾结构读起来没差别。怎么区分事件是外界给的动作是对象自己做的。写在箭头线上、位于斜杠左边的位置是事件斜杠右边是动作。还有一条土办法事件通常能加上“收到”两个字而不别扭收到按下启动动作加上“收到”就说不通收到开始播放别扭。写图时把这个位置摆对读图的人才能分清哪部分是被动接受、哪部分是主动执行。5.4 内部转换和自转换看着一样其实不一样易混点两者都表现为“事件发生了但状态没变”画在图上很容易被当成同一件事。为什么混结果确实一样——对象还待在原来的状态里。差别藏在过程里图上不显眼。怎么区分自转换是出去再进来退出动作和进入动作会重跑一遍内部转换是原地不动进出动作都不跑。判断标准是“这件事要不要重新走一遍进出的流程”。涉及资源重新初始化、状态需要重置的用自转换只是记个账、加个计数的用内部转换。这个区别在评审时最容易被放过上线后才发现初始化被反复执行性能莫名其妙地掉。5.5 状态图和交互图都在讲对象之间的事吗易混点状态图里也有多个对象参与的事件交互图里也讲对象似乎都在描述对象之间的互动。为什么混两者都属于动态建模都出现在概要设计阶段还经常被放在同一章里讲。怎么区分状态图看的是“一个对象内部的时间线”它回答“这个对象一生经历了哪些处境”交互图看的是“多个对象之间的一次协作”它回答“这一次交互里谁先给谁发消息、谁后回”。前者是纵向的、跨时间的后者是横向的、一次性的。图书系统里借书这个用例的状态图盯的是“图书”这一个对象怎么从在架走到已借出同一件事的顺序图盯的则是读者、借阅服务、库存、通知这几个对象在这一次借书里怎么你一句我一句。两张图不重复是互补的。6. 什么时候画、什么时候别画一张图的使用边界状态图不是万能图它擅长的事很具体一个对象的状态数量有限、状态之间的转换由明确的事件驱动、并且不同状态下对象的行为确实不一样。这三条同时成立画出来才有价值。缺一条图就会变成摆设。你要表达的东西更合适的图原因一个对象一生经历哪些处境、被什么推着换状态图状态数量有限事件驱动明确一件事分几步做完、每步谁负责活动图主体是流程不是单个对象一次协作里多个对象怎么互发消息交互图顺序图/通信图关注横向的消息时序系统里有哪些类、类之间什么关系类图静态结构不涉及时间一个对象有几十个状态、彼此乱连先拆对象再画图状态爆炸说明抽象没做对具体到什么时候别用有几种典型情况值得警惕。对象压根没有状态只有一堆方法那画状态图是硬凑画出来每格都只有一条进出线毫无信息量。业务流程跨了好几个角色每个角色各干各的这时候该用活动图或交互图状态图只能盯住其中一个对象画全了就是几张图拼在一起读者要来回跳。还有一种更隐蔽状态数量看着不多但转换关系是网状乱连的任意两个状态之间都可能有箭头。这不是状态图的问题是对象抽象没做对——大概率是把几个不同的对象塞进了一个状态机该拆成两个对象分别画。真正值得画的状态图通常满足一个特征把图贴到墙上产品、开发、测试三方都能指着某一格说“对这时候确实是这样”。如果只有画图的人自己看得懂那这张图的沟通价值是零。我刚工作那会儿画过一张自认为很完整的状态图二十多个状态箭头密密麻麻评审时没人提意见我还挺得意。结果联调阶段发现“缺纸”和“暂停”两个状态之间的转换根本走不通因为漏了一个事件代码里对应的分支压根没写。从那以后我每次画状态图都强制自己先列一张事件表——把所有可能发生的事件先横着写一遍再逐个问“这个事件在哪些状态下有意义、在哪些状态下该被忽略、在哪些状态下该被延迟”列完再动笔画。这一步多花二十分钟能省掉后面好几轮返工。这套资料里把事件、状态、转换三块拆得很细还配了借书还书两个完整用例照着走一遍比只看定义强得多。希望帮到你。本文还有配套的精品资源点击获取
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/10/29 5:06:01
Ponytail插件机制详解:轻量级聚合方案从原理到实操
2026/10/29 5:06:01
新概念英语在线学习平台背单词五法:间隔重复与主动提取
2026/10/29 5:06:01
AI生成前端代码质量校验:从设计令牌到可访问性的工程实践
2026/10/29 6:01:05
视频创作平台评测与AI剪辑技术解析
2026/10/29 6:01:05
context-mode工程实践:上下文管理机制设计与实现
2026/10/29 6:01:05
蒲公英内测分发平台使用指南与APK上传教程
2026/10/29 6:01:05
n8n工作流封装为业务表单的技术实现
2026/10/29 6:01:04
AI如何助力芯片工程师挖掘隐性设计知识
2026/10/29 5:56:04
AI编程入门实战:工具链、提示词与最小闭环项目路线
2026/10/29 0:00:09
服务器巡检报告模板怎么写:从检查项到PowerShell数据回填
2026/10/29 0:00:09
Zed编辑器Split Diff功能深度评测与优化指南
2026/10/29 0:00:09
OpenAI新模型定价解析:基础档便宜八成,快档贵六倍值不值?
2026/10/28 12:27:10
全国计算机二级C语言真题PDF高效刷题指南
2026/10/28 15:37:16
requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南
2026/10/28 12:37:46
递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现
2026/10/28 15:37:16
我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
2026/10/28 15:37:15
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026/10/28 15:37:15
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)