用例模型是UML里最容易被低估的一块内容。用例图谁都能画几个小人加圆圈,但真正能拿去评审、能撑起后续设计与测试的用例模型,十份里挑不出一份。用例图解决的是"系统边界在哪、谁跟系统打交道、系统对外承诺做什么"这三个问题,用例规格说明则把每个承诺落到可以验收的细节上。它不只服务于软考中级软件设计师那类考试题,实际项目里,需求评审、工作量估算、测试用例设计、接口划分,全都要回头翻这份东西。下面我按自己带项目的顺序,把用例图怎么画、用例规格说明怎么写、哪些坑必须绕开,一次讲透,新手能照做,有经验的可以对照检查自己的模型。
1. 用例模型到底解决什么问题
1.1 需求阶段的三种典型失控现场
做过几个项目就会发现,需求阶段翻车基本就三种姿势。第一种是需求方说了一堆业务规则,开发记了一堆功能点,双方都点头,等到验收时需求方说"我要的不是这个"。第二种是需求文档写成了一本小说,二十页流水账,谁也说不清系统对外到底承诺了几件事。第三种最隐蔽,功能都做对了,但没人知道系统边界在哪——哪些是系统做的,哪些是外部系统或者人工完成的,结果接口对不上。
用例模型就是治这三种病的。它把系统当成一个黑盒,只描述外部能看到的行为。我在一个景区舆情情感分析的项目里吃过亏,前期需求写的是"系统要能分析游客对景区的情绪倾向",开发直接埋头做情感词典和模型,做到一半才发现数据从哪来、多久采集一次、谁来看结果、看不了的时候找谁,全都没定。后来补的用例模型,光参与者就理出了三个:景区运营人员、舆情监控员、以及定时任务触发的采集调度(这个严格说是系统任务,我们用时间事件来建模)。边界一清,才发现"采集"这件事是外部数据服务负责的,我们只管消费,省了将近两周的无效开发。
注意:用例模型不是需求文档的替代品,它是需求的结构骨架。业务规则、字段定义、界面细节这些,该写还得写,只是先让骨架立起来,细节才有地方挂。
1.2 用例模型能做什么、不能做什么
说清楚它不能做什么,比说它能做什么更省事。用例模型不描述内部实现顺序,不描述数据结构和算法,不描述界面布局,也不描述性能指标。它描述的是"谁,为了达成什么目的,向系统发起了一次什么样的交互"。
能做的部分就很实在了。第一,确定系统边界。你在图上画一个方框,框内是用例,框外是参与者,这个框就是你的开发范围,也是你报价的范围。第二,对齐各方认知。用例名用的是业务语言,需求方能看懂,开发也能顺着往下拆。第三,支撑后续工作。每个用例往下可以生成详细规格说明,再往下可以生成测试用例,再往下可以划分成接口或者模块。我在做工作量估算时,习惯拿用例数乘以一个经验系数,虽然粗糙,但比按页面数估算靠谱得多,因为用例天然是按业务完整度切分的,一个页面对应半个用例的情况很常见。
一个常见误区是把用例图当成一堆功能的堆砌。用例不是功能清单,功能清单是"新增、删除、修改、查询"这类动词的排列,用例是"用户可以维护商品信息"这样一个能交付业务价值的最小完整交互。这条界线不划清,图会画得又大又废。
2. 用例图的核心元素拆解
2.1 参与者识别:先看谁从系统里拿到了东西
参与者(Actor)分三类:人、外部系统、时间。识别方法我一般用两句话去筛:谁从这个系统里得到了他想要的东西?谁给这个系统提供了它需要的东西?两个问题问完,名单基本就出来了。
新手最常犯的错是把"人"当成参与者。比如图书管理系统里,管理员和读者是两个角色,但如果出现"主管审批员"这种岗位,要判断的是他在这个系统里的行为跟管理员有没有区别。如果主管在系统里做的操作跟管理员完全一样,那他在用例模型里就是同一个参与者,用泛化关系也表示,或者干脆合并。反过来说,同一个人在不同场景下的行为差异巨大,那就该拆成两个参与者。我在一个后台系统里见过"运营"这个参与者,拆到最后变成了内容运营、活动运营、审核运营三个,因为审核运营有一整套独立的权限流程,跟内容运营的操作集合几乎不重叠。
外部系统参与者也容易被忽略。支付网关、短信服务、第三方地图、消息推送,这些只要跟你的系统有交互,就是参与者。把它们画上去有个直接好处:接口清单基本就从这些连线上出来了。
时间参与者是最容易被忘的。定时任务、超时触发、周期性对账,这类场景用时间作为参与者最准确。像舆情系统里"每天凌晨两点自动采集一轮",参与者就是时间,用例是"自动采集舆情数据"。不这么画的话,开发会把它当成一个内部方法,测试也不会去覆盖它。
| 参与者类型 | 判定标准 | 典型例子 | 常见坑 |
|---|---|---|---|
| 人 | 直接操作系统并从中获得价值 | 读者、管理员、审核员 | 把岗位当参与者,导致同一行为重复建模 |
| 外部系统 | 与目标系统有数据或控制交互 | 支付网关、短信平台、数据提供方 | 漏画导致接口清单缺失 |
| 时间 | 由时间条件触发的系统行为 | 定时对账、超时关单、周期采集 | 当成内部逻辑,导致测试遗漏 |
2.2 用例粒度:多细才叫合适
粒度是用例模型里最玄学的部分。我的判断标准有三条。第一条,一个用例必须能被一个参与者在一次连续的交互里完成,中间不需要切换到别的参与者去操作。第二条,一个用例应该对应一个可验证的业务结果,也就是说测试能写出明确的通过条件。第三条,用例不要小到只剩增删改查,也不要大到装下一个完整的业务流程。
举个例子,图书管理系统里"借书"这个业务,拆太细会变成"扫描读者卡"、"校验借阅额度"、"登记借阅记录"、"打印凭条",这四个当成用例会让图爆炸,而且单独任何一个都没有独立的业务价值。合太粗又会变成"管理图书借阅",这一个用例把借书、还书、续借、预约全包了,规格说明会写到崩溃,测试也拆不开。合理的做法是:借书、还书、续借、预约各是一个用例,因为它们由不同的触发条件发起,产生不同的业务结果,验证方式也完全不同。
有个实操技巧:如果一个用例的规格说明里,主流程超过十二步,或者备选流程超过五个,那就该考虑拆了。反过来,如果两个用例的规格说明有八成内容重合,那就该併。这个数字不是教条,是我踩过几次坑之后摸出来的经验值,用来当预警信号很管用。
2.3 四种关系:什么时候用,什么时候千万别用
用例图里的关系有四种:关联、包含、扩展、泛化。前三种几乎每次都要讨论。
关联就是参与者和用例之间的那条实线,谁发起谁连谁。这里有个细节,如果是参与者主动发起,用实线箭头指向用例;如果是系统主动把结果给参与者,很多团队也画,但严格来说那更接近输出。我一般画实线不带箭头,简单不容易争。
包含(include)表示一段被多个用例共用的行为。经典场景是登录校验、权限校验、必填项校验。但我强烈建议慎用。包含关系一旦滥用,画面会变成蜘蛛网,而且它描述的是行为复用,属于设计层面的东西,放在用例图上有时候是超前的。我现在只在两种情况下用包含:一是这段行为确实被三个以上用例共用,二是它的抽取能让评审者更容易理解。否则老老实实写进规格说明的公共章节。
扩展(extend)表示在特定条件下才会发生的一段行为,它不改动主流程。比如"借书"用例的扩展点是"读者存在超期未还",触发后执行"冻结借阅"。扩展的特点是可选、有触发条件、指向被扩展用例。这条关系最有价值的地方在于,它把异常和特例显式画出来了,评审时需求方一眼就能看到系统在这种情况下的行为。软考里判断题最喜欢考的就是包含和扩展的区别,记住一句话:包含是必然发生,扩展是条件发生。
泛化就是继承。参与者之间可以有泛化,比如"注册用户"是"游客"的泛化,游客只能浏览,注册用户还能下单、评价。用例之间也可以有泛化,但实际项目里用得少,一旦用多了说明抽象层次乱了。
| 关系 | 语义 | 是否必然发生 | 使用建议 |
|---|---|---|---|
| 关联 | 参与者与用例的交互通道 | 是 | 每个参与者至少要连一个用例,否则删掉 |
| 包含 | 被多个用例共用的子行为 | 是 | 三处以上复用才抽,否则写进规格说明 |
| 扩展 | 特定条件下的附加行为 | 否 | 用来显式表达异常与特例 |
| 泛化 | 一般与特殊的继承 | 依具体类型 | 参与者泛化常用于权限分层,用例泛化慎用 |
3. 一张能交付的用例图是怎么画出来的
3.1 从零散需求到用例清单的四步转化
第一步,把手上所有原始材料通读一遍,标出所有出现"角色"和"动作"的句子。会议记录、需求邮件、口头描述、旧系统的操作手册,全都算。这一步先不管对错,做加法。
第二步,把动作归拢成业务事件。判断依据是"这件事做完之后,业务状态有没有发生不可逆的变化"。比如"查询订单"不改变状态,但在很多系统里它也是一个独立的用例,因为它有明确的触发者和结果。这条规则要跟业务方确认,别自己拍。
第三步,给每个用例找一个主动发起者,找不到的用例要警惕。找不到发起者的用例通常有两种情况:一是它是内部处理逻辑,那就不该出现在用例图上;二是它是被别人触发的,那它是扩展或者包含。
第四步,做减法。把粒度不合适的合并或拆分,把名称改写成"参与者+动词+业务对象"的格式,比如"读者借阅图书"、"管理员维护图书信息"。名称写规范的好处是,后面做测试用例时可以直接对应。
3.2 绘图工具与布局:别让排版毁了一张好图
工具上,我分两种情况说。团队协作、需要版本管理的,用在线建模工具,好处是多人同时编辑不冲突、可以导出多种格式、评审时直接发链接。个人快速出图、只交一次作业的,用桌面版画图工具或者直接在本地的建模软件里画,导出成图片或者矢量图放进文档。软考答题是手绘,那就更要练布局,因为卷面分是真实存在的。
布局规范我总结了几条。参与者在框外,主要参与者放左边或者上边,次要参与者放右边或者下边,保持全图一致,别一个在左一个在右,评审的人来回找很累。用例在框内,按业务模块分区,同一模块的用例纵向排列。连线尽量减少交叉,实在避不开就调整位置。用例名写在椭圆里,字数多的话把椭圆拉宽,别换行换得七零八落。
命名上还有个小技巧,把"管理"这个词尽量少用。"图书信息管理"这种名字,一看就是为了凑数。改成"维护图书信息"、"查询图书信息",粒度反而清楚了。
3.3 一个完整案例:图书管理系统的用例图怎么落地
我拿图书管理系统走一遍,这是最经典的案例,也是软考最爱考的。
参与者先列:读者、图书管理员。如果再细一点,会有系统管理员负责账号和权限,还会有外部系统比如短信通知平台。第一版先画三个:读者、图书管理员、短信平台。
读者的用例:查询图书、借阅图书、归还图书、续借图书、预约图书、查询个人借阅记录、缴纳罚款。图书管理员的用例:维护图书信息、处理借阅、处理归还、管理读者账号、生成借阅报表。短信平台的用例:发送借阅提醒——这个用例的发起者其实是系统,所以它要么挂在时间参与者下面,要么作为"归还图书"或者"借阅图书"的扩展。
关系上,"借阅图书"包含"校验借阅资格",因为它被借阅、续借、预约三个用例共用。这里我要提醒一句,如果你觉得包含画出来太乱,就在规格说明里写成前置条件,我在正式交付的图里通常不画包含,只在教学或者讲复用的时候画。
扩展的使用场景:"借阅图书"的扩展点是"读者存在超期未还图书",触发条件成立时执行"冻结借阅权限"。这个扩展点必须写清楚,否则开发会漏掉,测试也不会覆盖。
最后检查三件事:有没有参与者什么用例都没连(有就删);有没有用例谁都没连(有就查是不是内部逻辑);框的大小是不是把所有用例都包住了,包括以后要扩展的。这第三点很重要,很多团队第一版把框画得刚好卡住当前用例,第二版加需求时框就破了,图得重画。
4. 用例规格说明:写不好等于没画图
4.1 标准模板与实际取舍
用例图的每个椭圆,都应该有一份规格说明。模板字段各家不同,我用的版本是这样:用例编号、用例名称、参与者、目标、前置条件、触发条件、主流程、备选流程、异常流程、后置条件、业务规则、非功能约束、待确认问题。字段不是越多越好,但前八个基本不能少。
| 字段 | 作用 | 写作要点 |
|---|---|---|
| 用例编号 | 唯一标识,方便追溯 | 用模块缩写加序号,如 BOOK-003 |
| 参与者 | 谁发起、谁参与 | 区分主动与被动,写清楚 |
| 目标 | 这个用例要达成什么 | 一句话,从参与者视角写 |
| 前置条件 | 开始前系统必须满足的状态 | 只写系统能校验的,别写"用户已登录"这种废话除非确实要校验 |
| 触发条件 | 什么事件启动这个用例 | 用户操作、时间事件、外部消息,都要写 |
| 主流程 | 一切顺利时的步骤序列 | 编号,每步一个动作,主语明确 |
| 备选流程 | 分支路径 | 必须说明在主线哪一步分出去的 |
| 异常流程 | 出错的路径 | 说明系统如何恢复到可用状态 |
| 后置条件 | 结束后系统的状态 | 包括成功和失败两种终态 |
注意:前置条件和后置条件最容易写成空话。检验标准是——一个测试人员只看这两栏,能不能判断用例执行前后系统状态有什么不同。不能,就重写。
4.2 主流程、备选流程与异常流程的写法
主流程写的是最顺利的那条路。写法上,每步用"参与者做什么"或者"系统做什么"开头,一句话一步,不要在一句话里塞两个动作。我见过写成一大段的,评审时根本没法逐条讨论。
举个例子,"借阅图书"的主流程:
- 读者在检索界面选择目标图书并提交借阅请求。
- 系统校验读者账号状态与借阅额度。
- 系统校验该图书的可借状态。
- 系统登记借阅记录,设定应还日期。
- 系统更新图书状态为已借出。
- 系统向读者反馈借阅成功信息。
六步,干净。你会发现第二步和第三步其实就是"包含"的那段校验逻辑,写在这里完全够用,图上不画也不影响。
备选流程要注明从主流程哪一步分出来。"读者借阅额度已满"这个备选流程,应该标明从第 2 步分出,然后写清楚系统提示什么、是否提供预约入口、读者取消后系统做什么。备选流程的价值在于,它是需求方最关心的地方——他们平时遇到的就是这些"特殊情况"。
异常流程处理的是技术性失败。数据库写入失败、外部短信服务不可用、网络超时。异常流程必须写清楚恢复动作,比如"登记失败则回滚借阅记录,向读者提示稍后重试,并记录错误日志"。很多团队只写"系统提示错误",这行等于没写,开发该怎么处理还是不知道。
4.3 业务规则与非功能约束别漏
业务规则是用例规格说明里最容易被忽略但价值最高的部分。比如借阅期限多少天、最多能借几本、续借几次、罚款怎么算,这些规则往往跨多个用例,写在一个用例里会造成重复,不写又会导致误解。我的做法是单独拉一张业务规则表,编号 BR-01、BR-02,然后在用例规格说明里引用编号。这样规则变更时,只需要改一处,再查一下引用了哪些用例。
非功能约束也一样。响应时间、并发量、数据保留期限、审计要求,这些散落在各个用例上,但真正影响架构的往往就那几条。把它们在关键用例里显式写出来,比写在需求文档最后的"性能要求"章节里更有效——因为写到那儿基本没人看。
5. 常见问题与排查技巧实录
5.1 用例模型十大错误速查表
| 问题现象 | 根本原因 | 处理方式 |
|---|---|---|
| 用例名全是"XX管理" | 粒度没切,逃避思考 | 改成"参与者+动词+对象" |
| 图里全是包含关系 | 把设计当需求画 | 只保留三处以上复用的,其余下移到规格说明 |
| 参与者连不到任何用例 | 角色冗余 | 直接删除或合并到其他参与者 |
| 用例谁都没连 | 内部逻辑混进用例图 | 移出图,转为内部处理或扩展 |
| 主流程写成一大段 | 没有逐条拆分 | 一步一动作,编号 |
| 备选流程不知从哪分出 | 没标分支点 | 注明"从主流程第 N 步分出" |
| 前置条件是"用户已登录" | 空话 | 改成系统可校验的具体状态 |
| 异常流程只写"提示错误" | 缺恢复动作 | 补回滚、重试、日志、人工介入路径 |
| 系统边界框每次都要重画 | 边界画太紧 | 预留扩展空间,按业务域划分 |
| 用例图和规格说明对不上 | 图和文档分开维护 | 用例编号做唯一索引,改一处连带检查 |
这张表我基本是拿来做自查清单的,出图前逐条过一遍,能省掉评审会上大半的返工。
5.2 评审会上最容易被问倒的三个问题
第一个问题:"这个用例的失败路径是什么?"只要你写了异常流程,这题就稳了。没写的话,评审会变成现场头脑风暴。
第二个问题:"这个参与者为什么跟另一个不能用同一个?"回答的核心是操作集合和权限边界的差异。如果差异只是"能不能看某张报表",那不应该拆参与者,而应该拆权限。这个区分很重要,很多团队把权限模型画进了用例模型,导致参与者数量膨胀。
第三个问题:"这个用例做完了,业务上谁能感知到?"这是在问用例的业务价值。答不上来的用例,多半是内部逻辑混进来的,或者是被人为拆得过细的功能点。
我个人的经验是,评审前自己先扮演一次抬杠的人,把每个用例按这三个问题过一遍,能自己答上来的留下,答不上来的先改掉再拿去评审。这比在会上被质问要体面得多,也快得多。
6. 软考应试与工程实践的那点区别
软考中级软件设计师的用例图题目,考的是识别参与者、判断关系、补全缺失用例。应试有几个固定套路值得注意。题目里出现"系统自动",多半是时间参与者或者扩展关系;出现"可以但不一定",基本就是扩展;出现"必须先"、"每次都",大概率是包含。填空题常考遗漏的用例,判断标准就是看哪个参与者没连上线,或者哪条业务链断了。
但工程实践跟应试有个明显的分歧点。考试喜欢让你画包含关系来体现复用,实际项目里我几乎不在图上画包含,因为复用是设计决策,过早画出来会限制实现方案。考试要的是标准答案,项目要的是可维护。两者不冲突,但你要知道自己当下在做哪件事。
还有一个实际差异是迭代。考试一道题就一次交付,项目里的用例模型是要持续维护的。我的做法是把用例编号做成唯一的锚点,需求变更时先查这个编号对应的图和规格说明,一起改。图用工具画,规格说明用表格维护,两边都引用同一套编号,这样即使半年后换人接手,也能顺着编号把上下文捞回来。
最后分享一个我踩过坑之后固定下来的习惯:每个用例的规格说明里留一栏"待确认问题",把当时没想清楚的点全写进去。这些点在评审时会被逐个问掉,问掉一个划掉一个。等所有待确认问题清空,这个用例才算冻结。比起等到开发阶段再发现没想清楚,这一栏能帮你省掉相当多的返工时间,也能让你在跟需求方沟通时有据可依。