画用例图的时候,最常听到的一句评价就是"这不就是画几个椭圆加几个小人吗?"——说这话的人,要么还没真正为需求挠过头,要么就是把用例图当成了交差的图形作业。我在这个系列前面几篇里聊过类图、包图这些偏设计阶段的图,它们解决的是"系统内部怎么组织"的问题;而用例图是另一条线上的东西,它解决的是"系统到底为谁、做什么事"的问题——这个"谁"和"做什么事",恰恰是很多项目烂尾的根源。
这个系列走到第4篇,该好好把这个"最简单也最难"的图拆开讲一讲了。不管你是正在画课程设计、准备软考中级软件设计师,还是真在项目里做需求分析,用例图画得好不好,直接决定了后边类图、时序图画得顺不顺。今天这篇不打算只堆语法规则,我想从根上讲清楚用例图的底层逻辑,再拿一个完整的案例带你走一遍,最后聊聊软考里那些看似刁钻、其实有套路的真题。
1. 用例图到底在解决什么问题:需求边界不清才是项目最大的坑
很多人一上来就问"用例图怎么画",但更值得先问的是"为什么要画用例图"。我在实际项目里见过太多这样的情况:需求文档写了一堆功能点,开发说"这个功能到底给谁用?"产品说"都有用",结果上线的功能一半没人点,另一半真正高频的却做得稀烂。背后的根因就是需求从来没有被结构性地梳理过。
1.1 用例图是分析阶段的"合同",不是设计阶段的"图纸"
先把这个系列前面的内容串一下:类图、包图、对象图这些属于静态结构视图,回答的是"系统由什么组成";时序图、活动图、状态图属于动态行为视图,回答的是"系统内部怎么协作"。而用例图既不关心内部结构,也不关心内部逻辑,它只关心一件事——系统外部的参与者,期望系统能够提供什么价值。
你可以把用例图理解成一份"需求合同":客户说"我要这套系统",用例图就是把"这套系统能让谁干成什么事"一条条写清楚。它不承诺"怎么实现",只承诺"能做到"。正因为这样,用例图是业务人员、需求分析师、开发人员三方之间唯一没有技术门槛的沟通语言。业务方看不懂类图,但绝对看得懂"读者可以借书""管理员可以上架新书"这种用例。
这也是为什么我说它难:画好用例图不需要懂技术,但需要对业务有足够深的理解。你能不能在杂乱无章的原始需求里,精准地识别出到底有几个参与者、每个参与者真正关心的目标是什么——这考验的是分析能力,不是画图能力。
1.2 用例图、类图、包图的分工,别再混为一谈
搜索相关关键词的时候,我发现"uml类图""uml包图"常常和"uml用例图"同时出现,说明很多初学者把这三种图当成同一层面的东西去学。实际上它们的定位差异非常大,我用一张表就能说明白:
| 图类型 | 所属阶段 | 回答的问题 | 使用场景 |
|---|---|---|---|
| 用例图 | 需求分析阶段 | 系统为谁提供什么价值 | 与业务方确认需求范围 |
| 类图 | 概要设计阶段 | 系统有哪些对象及其关系 | 指导编码实现,定数据结构 |
| 包图 | 概要设计阶段 | 类如何分组、模块如何划分 | 架构分层、模块解耦 |
拿图书管理系统来举例:用例图里画的是"读者-借书""图书管理员-处理借阅请求"这种业务层面的东西;类图里才会出现"Book类有id、title、isbn字段,和BorrowRecord是一对多关联"这种实现层面的设计;包图则把BookController、BookService、BookRepository这些类按层分包。三者是递进关系,用例图先框定"干什么",类图再定义"内部长什么样",包图最后划分"怎么组织"。不少软考真题就爱在这个地方设陷阱,给你一张图让你判断它属于哪个阶段、回答什么问题,理解了这三者的本质区别,这些题就是送分题。
2. 用例图的核心元素:参与者、用例和关系的正确打开方式
讲清楚用例图的定位之后,就该落到具体元素上了。用例图一共就那么几种图形符号:小人(参与者)、椭圆(用例)、方框(系统边界)、连线(各种关系)。符号本身十分钟就能记住,难的是怎么用这些符号正确地表达业务。
2.1 参与者不只是"人",识别参与者的三条硬标准
初学者最容易犯的一个错误,就是把"参与者"等同于"用户"。其实参与者是"与系统交互、并从系统中获得价值的外部角色",它可以是人,也可以是外部系统、硬件设备,甚至时钟。判断一个角色算不算参与者,我一般用三条标准:
第一,它是否独立于系统之外。参与者必须位于系统边界之外,系统管不到它的内部逻辑。比如图书管理系统里,"读者"在边界外,因为你的系统不负责管理读者内部的状态;但"图书管理员"虽然也是人,他的权限和操作由系统管理,所以他依然是参与者(与读者区分的是角色,不是是不是"人")。
第二,它是否有明确的目标需要系统配合完成。参与者不是"功能列表的读者",而是"带着目标来的人"。读者走进系统是想查书、借书、续借、预约,每一项都是明确目标;如果你发现某个角色在系统里"无事可做",那它大概率不是参与者。
第三,角色与角色之间是否真的需要区分。管理员也是人,读者也是人,为什么是两个参与者?因为他们在系统中追求的目标集合完全不同,权限边界也不一样。反过来,如果两个角色在系统里能做的事完全一样,那就合并成一个参与者,不要为了"身份好听"而拆成多个。
按照这套标准去看很多系统,你会发现所谓"游客用户""普通用户""会员用户"如果它们在用例图上能做的事没差异,就该合并;如果会员能比普通用户多一个"积分兑换"用例,那就必须分开。这个判断贯穿着整个过程,也是后边做题和做项目时反复要用到的。
2.2 用例是"完整目标",不是"功能点"
用例(Use Case)的定义是"系统外部参与者可观察到的、系统为其完成的一个完整目标"。关键词是"完整目标"和"可观察"。
什么叫完整目标?从参与者的角度看,这件事做完之后应该得到一个有价值的结果。对读者来说,"查询图书"是一个完整目标——输入关键词、看到结果,得到信息;但"输入关键词"不是完整目标,因为它只是查询的中间步骤;"把查询结果按出版年份排序"也不是完整目标,它只是查询功能的一个细节需求。这些中间步骤和细节需求,应该在用例描述里写清楚,而不是拆成用例。
什么叫可观察?就是这个目标完成的结果必须对参与者可见。系统内部的数据清洗、缓存更新、日志记录,参与者看不到也不关心,这些就不该作为用例出现。我审过不少新人画的用例图,动不动就出现"系统自动清理过期借阅记录"这种用例——这确实是系统要干的事,但它不是"参与者可观察的完整目标",它是系统内部的定时任务。这个用例应该被描述为"管理员-查看逾期列表"或者干脆写进时序图,而不是画在用例图里。
以图书管理系统为例,合理的用例应该是一批这样的东西:读者-查询图书、读者-借阅图书、读者-归还图书、管理员-处理图书借出、管理员-处理图书归还、管理员-维护图书信息、管理员-处理罚款。每一个都是读者或管理者能够感知到结果的完整事件。
2.3 五种关系里,最考人的是include和extend
用例图里的关系分为五种:关联(Association)、包含(include)、扩展(extend)、泛化(Generalization)、依赖(Dependency)。其中关联是参与者与用例之间的实线,无需多说;泛化用在参与者或用例之间的继承;依赖用法比较少见,早期UML版本里还会出现在用例之间,现在基本被包含/扩展取代了。
最让人头疼的是include(包含)和extend(扩展)之间的区别。我直接用最朴素的方式来解释:
包含关系(虚线箭头指向被包含的公共用例,箭头上标《include》),代表的是"一定会做"的公共步骤。比如"借阅图书"这个用例,在执行过程中"验证读者身份"这一步无论如何都跑不掉,而且"预约图书"可能也需要"验证读者身份"。把这个公共步骤抽出来作为一个独立的"读者身份验证"用例,再让"借阅图书"和"预约图书"都包含它,这就是include。关键特征是:基用例在执行时,被包含用例必须被执行。
扩展关系(虚线箭头指向基用例,箭头上标《extend》),代表的是"可选发生的附加行为"。比如"借阅图书"这个主流程完成后,如果读者的借阅数量达到了上限,系统要额外执行"借阅超限处理";但大多数时候借阅是正常的,这个处理根本不会发生。扩展用例只有在满足特定条件的时候才会插入到基用例的扩展点。关键特征是:基用例独立存在且完整,扩展用例是锦上添花、条件触发的。
一句话记住这两者的差别:include是"每次都得干的公共步骤",extend是"特定条件下才插入的额外流程"。软考真题里最常见的陷阱就是给你一个场景,问应该选include还是extend。判断方法很简单——把这段额外逻辑抽掉,基用例还完不完整?如果完整,那就是extend;如果基用例缺少它就运行不下去,那就是include。
3. 用例粒度怎么把握:从业务用例到系统用例的层次感
如果说关系是语法,那么粒度就是用例图的"灵魂"。很多用例图画出来不是错,而是"没劲"——要么一个椭圆塞下整个系统,要么把用例图画成了功能列表。我见过最极端的用例图,整个系统就一个用例叫"管理系统",旁边坐着一个"管理员",看完不知道该说什么。
3.1 粒度太粗等于没分析,粒度太细等于没设计
用例粒度本质上取决于你画这张图的目的是什么。如果是给高层做项目立项汇报,你可能只需要到"业务用例"级别,把整个系统拆成五六大块就够了;但如果你是要指导后续的类图和开发,就必须细化到"系统用例"级别,也就是能清晰对应到具体的交互流程。
这里说的业务用例和系统用例,不是两种不同的图形,而是同一张图上的不同抽象层次。我给团队定过一个非常实用的判断标准:把用例的名字念出来,如果你觉得"这件事做完了,用户的问题解决了吗"的回答是"解决了",那么这个粒度就是合适的;如果回答是"可能是吧,还要再做点什么",那就太粗了;如果回答是"这只是其中一步",那就太细了。
拿图书管理来说:
- 太粗的用例:"图书管理"——这不是一个用例,这是一个功能模块,里面塞了上架、下架、改价格、查库存一堆事,参与者无法通过这一个步骤完成任何明确目标。
- 合适的用例:"管理员上架新书""读者预约图书"——每个用例都表达了一个参与者可以感知的完整目标。
- 太细的用例:"读者选择借阅数量""管理员点击确认按钮"——这是操作步骤,是用户界面层面的交互,不是用例。
3.2 我用的四步粒度校验法
具体操作的时候,我一般用四个问题逐一校验每个用例:
这个用例有没有明确的主语(参与者)和谓语(目标动作)?没有主语的用例一定有问题。
这个用例的完成结果对参与者是否可见、是否有业务价值?"系统发送通知"这种对参与者来说算可见,"系统更新数据库字段"这种就不算。
这个用例能否独立执行、独立验收?如果一个用例依赖另一个用例执行完才能开始,那通常意味着两者的边界没划对,或者存在真正的include/extend关系。
整个用例图去掉某个用例之后,参与者是不是还有其他途径达成类似目标?如果有,说明两个用例之间可能存在泛化或包含关系,需要重新组织。
我自己画用例图的时候,从第一阶段到定稿通常会经历三轮删减。第一轮把操作步骤删掉,第二轮把系统内部动作删掉,第三轮把相互重复的用例合并。每一次删除都要能说出理由,说不出理由的先留着,等想明白了再决定去留。这个过程看似浪费,其实是需求分析最值钱的部分。
3.3 为什么粒度问题在软考里也很重要
软考中级软件设计师的案例分析题,有时会直接给出一段需求描述,让你"根据需求画出用例图"或者"指出图中存在的错误"。这种题表面考画图,实际上就是考粒度判断。我曾经见过一道真题,参考答案里明确把"用户注册"作为用例,但很多人在它下面又拆出了"填写注册信息""验证手机号""设置密码"三个用例——后者就是典型的"太细了",它们是注册流程里的步骤,应该写进用例描述,不是独立用例。
还有一类真题会故意把"查询图书"和"高级查询"画成包含关系,问你错在哪里。实际上"高级查询"是"查询图书"的可选条件分支,应该用extend而不是include。这类题做错的人非常多,原因就是前面说的那一点:没有抓住"抽掉后基用例是否完整"这个判断核心。
4. 实战推演:从一段杂乱需求到一张合格的图书管理系统用例图
理论讲再多,不如完整走一遍。下面我用一个非常经典的"图书管理系统"需求来做全流程推演。这个场景在软考题目和学生项目里出现频率极高,搜索引擎里"图书管理系统用例图"能排到热词榜,说明它的代表性确实强。
4.1 原始需求长什么样,怎么找到参与者
假设我们现在拿到的原始需求描述是这样的(我特意保留了它零散、口语化的样子):
"这个系统需要支持图书的查询和借阅。读者进入系统可以登录,可以查询图书信息,如果有库存就能借书,到期前可以续借,超过期限要交罚款。管理员负责图书的入库和出库,还要管理读者的信息和处理罚款。系统在图书到期前要给读者发提醒。"
面对这样一段文字,第一步不是急着画椭圆,而是先圈出"名词性角色"。这段需求里出现了"读者""管理员"两个明确角色,还有一个隐藏角色值得考虑:系统自动发提醒这件事,是人触发的还是系统定时触发的?如果是系统自动在到期前发,那就意味着有一个"时钟/定时器"参与者——这在很多系统里都会被漏掉。
我按前面说的三条标准一一过:
- "读者":在系统外部、目标是查询/借阅/续借/还书,是参与者。
- "管理员":在系统外部、目标不单是借书而是管理整个图书流程,是参与者。
- "定时器":虽然它不是人,但它独立于系统外部,定期触发"发送到期提醒"这个目标,是参与者的一个合法变体。不过要注意,如果你们的项目里把提醒功能看作管理员操作的子步骤,那也可以不单列参与者和用例,毕竟分析阶段可以根据项目规模灵活取舍。
4.2 从角色目标推出用例清单
确定参与者之后,逐个问"他为什么要用这个系统"。这个过程我会用一张简单的表格来整理,表里记录参与者、目标描述、是否核心。这个习惯推荐大家保留,它是一个过渡到用例清单很顺畅的中间产物:
| 参与者 | 目标描述 | 是否核心 |
|---|---|---|
| 读者 | 查询图书信息 | 是 |
| 读者 | 借阅可借图书 | 是 |
| 读者 | 续借即将到期图书 | 是 |
| 读者 | 归还已借图书 | 是 |
| 读者 | 查询个人借阅记录和罚款情况 | 否(但推荐) |
| 管理员 | 管理图书基本信息(上架、下架、改信息) | 是 |
| 管理员 | 处理图书借出和归还 | 是 |
| 管理员 | 管理读者账户(注册审核、停用等) | 是 |
| 管理员 | 处理罚款 | 是 |
这些就是初始用例。注意几个细节:
- "登录"我没有放进核心清单,因为登录通常是所有业务流程的公共前置步骤,更适合抽成被包含的公共用例,而不是每个参与者都反复关联的独立用例。当然,如果系统中"游客"也能访问一部分功能,登录就得慎重处理——这种情况下一般会引入"游客"这个参与者,而"登录"作为独立用例让游客变成读者。
- "发送到期提醒"我暂时留着,后面考虑是作为定时器参与者的独立用例,还是基于"读者"的某个用例的扩展点。
- "查询个人借阅记录和罚款情况"虽然不是原始需求明文,但从业务完整性上讲,读者总得知道自己在系统里有几本没还、欠了多少罚款,否则整个借阅闭环是不成立的。这是需求分析里最常见的"隐含需求",你把它补出来,业务方通常都会认可。
4.3 梳理关系:包含、扩展还是泛化
用例清单列出来以后,就可以开始连线了。这一步最容易出错的不是"连不连",而是"用哪种方式连"。
先处理公共步骤。读者要借书,必须首先登录并验证身份;管理员处理借出,也必须验证自己的权限。这时候把"身份验证/登錄"抽成一个独立用例,然后让"借阅图书""处理图书借出""续借图书"等所有需要合法身份才能执行的用例都include它。这个抽法在正规项目里非常常见,也是考题里频繁出现的考点。注意这里是用例之间的虚线箭头,箭头指向"身份验证",箭头上标《include》。
再处理可选分支。续借有一个典型场景:读者借的书可以续借,但如果这本书已经被其他读者预约了,就不能续借;或者读者有逾期未还的图书,也不能续借。这个"不能续借"的流程不是每次续借都会发生,而是在特定条件下触发,所以它应该作为"续借图书"的extend扩展用例,触发条件就是"存在未还逾期图书或图书被预约"。
同理,"处理罚款"也可以用extend的方式挂在"归还图书"上:正常归还,流程就结束了;如果归还时发现逾期,才进入罚款处理环节。这个扩展关系特别能体现业务分支,放在用例图上一眼就能看懂"什么情况下会发生什么"。
至于参与者之间的泛化,最典型的就是"游客"与"读者":游客能够查询图书,读者继承游客的功能并且还能借书、续借、预约。在用例图上,游客和读者两个小人之间画一条带空心三角箭头的泛化线,箭头指向游客,意思就是"读者是一种游客的扩展"。如果需求里没有游客能使用的功能,那就不用画泛化,保持"读者""管理员"两个独立参与者即可。
4.4 最终用例图的完整信息结构
把所有信息组合起来,一张图书管理系统的用例图大概包含这些内容:
- 系统边界:一个大的方框,框内是系统的所有用例,框外站着参与者。边界这个名字一般是"图书管理系统"。
- 参与者:读者、管理员(如果做扩展,还有游客、定时器,视需求规模而定)。
- 核心用例:查询图书、借阅图书、归还图书、续借图书、预约图书、管理图书信息、管理读者账户、处理罚款。
- 公共用例:身份验证(登录)。
- 扩展用例:借阅超限处理、续借失败处理、逾期罚款处理。
我不敢说这张图满足所有教材的苛刻规范,但在实际项目里,做到这个程度已经足够让开发人员和测试人员各自去拆任务、写用例了。如果你打算在软考答题里画图,还需要额外注意三角形的方向、箭头的虚实——这些细节我就放到下一节一起说,因为它们往往就是扣分点。
5. 软考中级软件设计师里,用例图真题是怎么考的
之所以要把软考单独拿出来讲,是因为题库数据里"软考中级软件设计师用例图真题"是一个大热搜索词。在考试语境下,用例图的考法跟项目实战里"画一张图"的考法很不一样,前者更多是"给图找错"和"补全缺失",后者才是"从零画图"。做题技巧和画图技能有重叠,但需要额外练一套判断方法。
5.1 真题常见的三种命题角度
第一种是"识图辨析"。题目给出一张某系统的用例图,让你判断哪些地方画错了,常见错误包括:include箭头方向反了、extend箭头方向反了、参与者放在了系统边界内部、用例与参与者连成了用例对用例的关系、泛化箭头方向错了等等。此类题属于概念判断题,难度不高,但它检验的是最基础UML规范是否牢固。
第二种是"补全用例"。给你一段需求描述,给出一张不完整的用例图,让你补几个缺失的用例或参与者。这种题考的是需求理解能力,做题时的核心思路就是我前面讲的三条参与者识别标准和四步粒度校验法。从需求文本里圈定语干,再判断谁是参与者、谁是目标,答案自然就出来了。
第三种是"概念辨义"。不直接考图,而是用文字描述一个场景,问你应该用include还是extend,或者问某段描述属于用例图的哪个元素。这种题有时候比看图还容易错,因为文字描述往往故意说得模棱两可,比如"管理员可以查看统计报表,如果报表导出失败,则记录日志"——后一句"记录日志"应该被描述成扩展用例还是系统内部动作?答案是:如果"导出统计报表"是核心目标,"记录日志"是导出失败时才执行的附加行为,画成extend没问题;但如果这个日志只为系统自身排错服务、对参与者不可见,它压根不该出现在用例图里。考试时这种"要不要出现"比"用哪种关系"更常考。
5.2 做题的三步定位法:主语、目标、完整性
我自己参加考试和辅导同事时,总结了一套三步定位法,遇到任何用例图判断或补全题都先用这三步过一遍,正确率很稳定。
第一步,找主语。图上出现或者题干中出现的每一个动作,先问"谁在做这件事"。找不到主语的动作、主语不是参与者、或者主语是系统但动作对参与者不可见——那么它是一个可疑的用例,大概率不成立。
第二步,找目标。确认这个动作完成后,参与者是不是得到了一个可感知的完成结果。得到的就是用例,得不到的可能是操作步骤、系统内部逻辑或者前置条件,需要从用例清单里剔除或降级为用例描述。
第三步,验完整性。把剩下的候选用例代入include/extend判断框架:这个用例抽掉公共步骤后是否完整?如果某个候选用例总依赖另一个用例,考虑它们是不是本来就是包含关系;如果某段说明是"在特定条件下发生",考虑它是不是扩展关系。三步走完,答案基本就浮出水面了。
5.3 一个高频失分点:边界用对了图,却忽略了箭头方向
很多人实际画图或者做题时栽在include和extend的箭头方向上。UML规范是:include的虚线箭头从基用例指向被包含用例,也就是"借阅图书"指向"身份验证"方向;extend的虚线箭头从扩展用例指向基用例,也就是"借阅超限处理"指向"借阅图书"方向。两个箭头的指向完全相反。
记法上有个口诀:include是"依赖"——基用例依赖被包含用例,所以箭头跟着依赖走;extend是"扩展"——扩展用例去扩展基用例,所以箭头从扩展者指向被扩展者。刚开始的时候容易搞混,我自己早期的办法是在脑子里把include理解为"我要带上它",把extend理解为"它来插入我"。多练几道真题,方向基本就形成条件反射了。
另外,箭头是虚线,别画成实线,边上要标注《include》或《extend》的版型。软考阅卷对这类形式化细节是有扣分标准的,细节决定过不过。
6. 最后再顺手补充几个实操经验:工具、命名和画图时最容易犯的错
写完前面这些,关于用例图这个主题的核心内容就差不多了。最后用我平时在实际项目中积累的几条实操经验来收个尾,当作给看完这篇、准备自己动手的读者一点加速度。
工具方面,画用例图不需要专门的复杂建模工具,StarUML、draw.io、ProcessOn、Visio都能胜任。如果你要输出规范的、带UML版型标注的关系线,StarUML和draw.io比ProcessOn更顺手,因为后者在控制版型标注的细节上偶尔不够严谨。如果在软考纸笔答题阶段,那就更简单,草稿纸上画清楚"小人、椭圆、方框、虚线箭头、版型字样"这几个要素就够了,画功好不好不影响拿分,规范性才影响。
命名方面,我强烈建议用例名称统一用"动词+名词"短语,比如"查询图书""处理罚款""生成统计报表",不要用"图书查询功能""图书信息维护模块"这种半功能半用例的名字,也不要用纯名词"图书查询"这种动词缺失的写法。一个用例名念出来应该是一个能让人听懂的动作目标。参与者的命名统一用名词短语,"读者"就是读者,不要出现"读者用户角色"这种叠床架屋的说法。
还有一个我见过无数次、属于隐蔽低级错误的问题:把系统边界画得太小,以至于一些后续迭代中新增的功能只能往边界外"外挂"。系统边界其实不是一个固定不变的硬壳,它是随着需求范围实时调整的。推荐的做法是先把参与者和用例的关系连线画清楚,最后再框系统边界。如果你一开始就画了一个框然后拼命往里塞,很容易为了"装进去"而强行合并或裁剪用例。
最后说一个很多人忽略的点:用例图是读者和开发沟通的起点,不是终点。一张好的用例图后面往往还要配一个用例描述文档,把每个用例的主流程、备选流程、前置条件、后置条件写清楚,类图和时序图才能有据可依。这也是为什么我每次带项目,都要求用例图先评审再进入设计阶段——因为用例图一旦错了,后面全是在错误的地基上盖楼。把用例图画清楚这件事,花的时间永远值得。