很多刚接触软件工程的人第一次画UML用例图时都会陷入一种奇怪的纠结对着空白的画布脑子里全是这个该不该画成小人这个虚线箭头到底朝哪边为什么别人画的图看起来干净利落我画的像蜘蛛网。我带过几届做课程设计的学生也帮不少转行的朋友梳理过需求文档发现用例图这块的坑高度集中——不是不会用工具而是没搞明白用例图到底在表达什么、边界在哪里。用例图Use Case Diagram是UML里最轻的一类图轻到很多人觉得它没什么技术含量随便画画交差。但恰恰是这张图决定了你后续需求分析、功能拆解、测试用例设计的走向。画歪了后面全歪。这篇内容我打算把用例图从为什么画到怎么画好完整拆一遍包括那些教程里很少提、但实际画图时天天遇到的判断难题。不管你是刚学软件工程的学生还是需要写需求文档的开发者、产品岗都能从里面找到能直接抄作业的东西。1. 先搞清楚用例图到底在解决什么问题1.1 用例图不是功能清单的图形化我见过太多人把用例图画成了功能列表配小人。左边一个用户右边拉出十几条线每条线连一个椭圆椭圆里写着登录注册查询修改删除。这种图画完除了证明你会用绘图工具几乎没有任何分析价值。用例图的本质是从外部视角描述系统能提供什么服务。注意两个关键词外部视角、服务。它不关心系统内部怎么实现不关心数据库怎么设计只关心谁参与者为了达成什么目的会跟系统发生什么交互。所以用例的命名应该是一个动宾结构的完整目标比如提交订单查询物流状态而不是订单模块物流功能这种名词化的模块名。这个区别很关键。当你用目标来思考时你会自然地去问这个目标是谁发起的他为什么要做这件事做完之后系统状态发生了什么变化这些问题会逼着你去补全需求里模糊的地方。而用功能来思考你只是在罗列罗列是不产生洞察的。1.2 参与者的识别比用例识别更容易出错新手往往觉得找用例难其实找参与者才是最容易翻车的地方。参与者Actor不一定都是人。定时任务、外部系统、硬件设备只要它主动跟你的系统交互它就是参与者。比如一个电商系统里支付网关就是一个外部参与者它会在支付完成后回调通知你的系统。另一个常见误区是把参与者的角色和具体的人混为一谈。同一个物理人可能同时是买家和卖家这时候在图上应该画成两个参与者因为他在两种角色下的目标和权限完全不同。我一般建议学生先列一张角色-目标对照表把每个角色想达成的核心目标写下来再决定图上画几个小人。这张表比图本身更有价值因为它是你后续写用例规约的素材。还有一个容易忽略的点参与者之间可以有泛化关系。比如VIP会员和普通会员都继承自会员VIP能做的事普通会员不一定能做。这种继承关系在图上用带空心三角的实线表示箭头指向父参与者。很多人不知道参与者能泛化结果把公共用例重复画了好几遍图面乱成一团。1.3 系统边界画不画取决于你想强调什么系统边界就是那个大方框把所有用例框在里面参与者在框外。有些教程说必须画有些说可以省略。我的经验是当你的系统需要和多个外部系统交互时边界框一定要画因为它能清晰地划分哪些是我的责任哪些是别人的责任。如果只是单系统、单用户群的简单场景边界框可以省省了图更干净。画边界框的时候框的命名也有讲究。不要写系统两个字就完事要写清楚这个系统的定位比如在线课程学习平台仓库库存管理子系统。名字起得越具体后面讨论需求时越不容易跑偏。我见过一个团队边界框上写着后台结果讨论到一半大家才发现有人理解的后台是运营管理端有人理解的是服务端API白吵了半小时。2. 用例之间的四种关系画错一个整张图就废了用例图里最让人头大的就是关系线。包含、扩展、泛化、关联四种关系长得像但含义完全不同。我下面逐个拆重点讲什么时候该用哪个而不是干巴巴地背定义。2.1 关联关系参与者与用例之间的那根实线关联关系就是参与者和小人连到用例的那根实线没有箭头或者可以有箭头表示谁发起。它表示参与者与用例之间存在交互。这是最基础的关系但有个细节很多人做错一个用例可以被多个参与者关联这时候要判断谁是主参与者、谁是次参与者。主参与者是主动发起用例的人次参与者是被动参与的人。举个例子查询成绩这个用例学生是主参与者他主动查教务系统可能是次参与者提供成绩数据。在图上主参与者的关联线可以带箭头指向用例次参与者用普通实线即可。这个区分在写用例规约时很有用因为主参与者的操作流程才是你描述的重点。提示不要给关联线加箭头来表示数据流向。关联线不是数据流箭头只表示谁发起。数据流是另一类图数据流图该干的事混在一起画会让图产生歧义。2.2 包含关系把公共步骤抽出来但别抽过头包含关系Include用带箭头的虚线表示箭头指向被包含的用例。它的语义是基用例在执行过程中一定会执行被包含的用例。典型场景是把多个用例里重复的步骤抽成一个公共用例。比如下单退款查询订单三个用例都需要验证用户登录状态那就可以抽一个身份验证用例让前三个都包含它。这样改需求时只改一处图也更清爽。但包含关系有个大坑抽得太细会把图变成流程图。我见过有人把输入用户名输入密码点击登录都抽成独立用例然后用包含关系串起来。这不是用例图这是把代码逻辑画成了图。用例的粒度应该保持在对用户有价值的目标这个层级内部步骤不属于用例图的表达范围。判断标准很简单如果一个用例单独拿出来给用户看用户会觉得这也算一个功能那它就抽得太细了。2.3 扩展关系可选行为用扩展别用包含硬凑扩展关系Extend也是带箭头的虚线但箭头方向和包含相反箭头从扩展用例指向基用例。语义是扩展用例在特定条件下可能会插入到基用例的执行流程中是可选行为。典型例子下单是基用例使用优惠券是扩展用例。用户下单时不一定用优惠券用了才触发扩展。图上画法是使用优惠券虚线箭头指向下单。包含和扩展最容易混淆我总结一个判断口诀包含是必须做扩展是可能做包含的箭头指向被抽出的公共部分扩展的箭头指向被扩展的主体。再补一句包含关系里基用例离开被包含用例就完成不了扩展关系里基用例离开扩展用例照样能独立完成。用这个标准去套基本不会错。2.4 泛化关系用例也能继承但别滥用用例泛化用带空心三角的实线表示箭头指向父用例。它表示子用例继承了父用例的行为并可以增加或覆盖自己的行为。典型场景是支付作为父用例支付宝支付银行卡支付余额支付作为子用例。泛化在用例图里用得相对少因为大部分不同方式其实更适合用扩展或者直接在用例规约里描述。我一般只在子用例确实有独立参与者或者独立触发条件时才用泛化。如果只是内部处理逻辑不同画泛化反而增加了图的复杂度收益不大。下面这张表把四种关系放在一起对比画图时对着查关系类型线条样式箭头方向语义典型场景关联实线可无箭头或指向用例参与者与用例交互用户发起某个操作包含虚线指向被包含用例必定执行的公共步骤多个用例都要验证登录扩展虚线指向基用例条件触发的可选行为下单时使用优惠券泛化实线空心三角指向父用例子用例继承父用例多种支付方式3. 从零画一张用例图的完整操作链路光讲概念没用我拿一个模拟的图书借阅管理系统走一遍完整流程。这个场景足够典型参与者、用例、关系都能覆盖到你可以直接套用到自己的项目上。3.1 第一步列参与者清单别急着开画打开绘图工具之前先拿张纸或者开个文档把参与者列出来。图书借阅系统里我初步想到的参与者有读者、图书管理员、系统定时任务催还提醒、外部支付系统罚金缴纳。这里有个判断技巧问自己谁会对系统说我要做某事。读者会说我要借书我要查借阅记录管理员会说我要登记新书我要处理归还定时任务不需要说话但它会在每天固定时间触发发送催还通知支付系统会在罚金支付完成后回调更新缴费状态。这四个都是参与者。列完之后再检查一遍有没有哪个参与者其实只是另一个参与者的特例比如学生读者和教师读者如果他们的借阅权限只是数量不同那可以合并成一个读者参与者权限差异在用例规约里写清楚不必在图上拆成两个小人。图面简洁比精确到每个细分角色更重要。3.2 第二步按参与者逐个找用例用目标句式命名针对每个参与者问他想达成什么目标。读者想达成的目标借书、还书、查询借阅记录、缴纳罚金。管理员想达成的目标登记新书、注销旧书、处理借还、维护读者信息。定时任务的目标发送催还通知。支付系统的目标回调缴费结果。命名的时候统一用动宾结构比如借阅图书而不是借阅登记新书而不是新书登记。统一句式能让图看起来专业也方便后续和用例规约对应。这一步还要做一件事合并重复目标。借书和还书虽然都是读者的目标但它们是两个独立用例不能合并。查询借阅记录和查询罚金记录如果查询逻辑高度相似可以考虑合并成查询个人借阅信息但要看用户是否真的把它们当成一件事。合并的尺度是用户会不会在同一个操作意图下完成它们。会就合并不会就分开。3.3 第三步补关系先画关联再考虑包含扩展参与者和小人连好用例外先只画关联线把主干搭起来。这时候图应该是一堆小人连着各自的椭圆看起来有点散但结构清晰。然后审视有没有多个用例共享的步骤读者借阅图书和缴纳罚金都需要验证读者身份管理员处理借还和维护读者信息都需要管理员登录验证。这两个可以抽成包含用例。注意抽出来的用例命名要体现被复用的性质比如身份验证就比验证读者身份更通用因为管理员那边也能用。再审视有没有可选行为读者借阅图书时如果读者有逾期未还的书系统会触发冻结借阅权限。这是一个条件触发的行为适合用扩展。画法是冻结借阅权限虚线箭头指向借阅图书。最后审视泛化这个系统里缴纳罚金可能支持在线支付和线下缴纳两种方式如果这两种方式的参与者不同在线支付涉及外部支付系统线下缴纳涉及管理员那可以用泛化父用例是缴纳罚金子用例是在线缴纳和线下缴纳。如果参与者相同、只是内部流程不同就别画泛化在规约里写清楚即可。3.4 第四步布局调整让图能一眼看懂关系补完之后图往往会变乱。这时候要做布局优化几个实用原则参与者放在边界框的左右两侧主动发起方放左边被动接收方放右边。读者、管理员放左边支付系统、定时任务放右边。高频用例放中间低频放边缘。借阅、归还放中间注销旧书、维护读者信息放边上。包含用例放在基用例附近用虚线连过去别让它飘到图的最角落。避免线条交叉。如果两条线必须交叉调整用例位置或者用绘图工具的跳线功能。交叉线是图面混乱的头号元凶。我一般会画两到三版第一版求全第二版求简第三版求清晰。第一版把所有想到的都画上第二版删掉冗余的、合并重复的第三版调整布局和命名。这个过程花不了多少时间但出来的图质量差别很大。4. 那些教程不写、但实际画图天天遇到的判断难题概念和流程讲完了下面这部分是我觉得最有价值的内容——实际画图时那些卡住的瞬间到底该怎么判断。这些判断没有标准答案但有经验法则。4.1 一个操作到底算一个用例还是多个用例这是最高频的问题。比如用户管理包含新增、删除、修改、查询。是画一个管理用户用例还是画四个用例我的判断标准是看操作的目标是否独立。新增用户和删除用户目标完全不同用户在做这两件事时的意图、输入、系统反馈都不一样应该拆成两个用例。而修改用户信息和查询用户信息如果查询是为了修改做铺垫且通常在同一界面完成可以考虑合并成维护用户信息。但如果查询有独立的入口比如管理员只想看看某个用户的状态不修改那就该独立。再给一个更硬的判断如果两个操作对应不同的权限控制就拆开。新增和删除往往权限不同拆开查询和修改权限也可能不同拆开。权限是需求分析里非常实在的维度用它来判断用例粒度比凭感觉靠谱。4.2 参与者能不能主动触发系统内部的动作有人问系统自己定时执行的清理任务算不算用例算但参与者是时间或者调度器。UML里允许把时间当作参与者画一个小人标注定时器或者调度任务即可。这个用例的触发条件是时间不是人但它在图上依然是一个正常的用例关联到时间参与者。还有一种情况系统A调用系统B的接口。对系统B来说系统A就是参与者。这时候系统A画在系统B的边界框外面用关联线连到被调用的用例。不要因为都是系统就把它们画在同一个框里边界框的意义就是划分责任范围。4.3 用例图要不要画异常流程不画。用例图是目标层面的概览图异常流程属于用例规约Use Case Specification的内容。用例规约里会有基本流、备选流、异常流那才是描述细节的地方。用例图上如果画异常图会爆炸而且异常分支往往很多画不全反而误导人。但有一个例外如果某个异常流程本身就是一个独立的用户目标那它应该是一个独立用例。比如申请退款对用户来说是一个独立目标它不是下单的异常流程而是一个平级的用例。判断标准还是那个用户会不会专门为了这件事来找系统。4.4 图上的用例数量控制在多少合适没有硬性规定但经验值是单张图 10 到 20 个用例比较舒服。少于 10 个可能拆得不够细或者系统太小多于 20 个图开始难读建议按子系统或者按参与者分组拆成多张图。拆图的时候每张图保持一个清晰的边界框框名体现子系统。比如图书借阅系统可以拆成借阅管理图书维护读者管理罚金管理四张图。拆图不是偷懒是让每张图聚焦一个主题读者看的时候不用在一堆椭圆里找目标。4.5 工具选型别在工具上纠结太久用例图对工具的要求极低能画椭圆、小人、虚线、实线、空心三角就行。常见的几类工具工具类型代表适合场景注意点专业建模工具各类UML建模软件需要生成代码、导出多种图学习成本高小项目没必要通用绘图工具各类在线白板、流程图工具快速出图、团队协作图形库要手动对齐注意规范文本转图工具支持PlantUML语法的工具版本管理、快速迭代布局自动但微调受限我个人的习惯是需求讨论阶段用通用绘图工具因为改起来快拖拽就行定稿归档用文本转图工具因为文本可以进版本库改一个词重新生成图永远和文档同步。PlantUML画用例图的语法很简单几行代码就能出一张图startuml left to right direction actor 读者 as R actor 管理员 as A rectangle 图书借阅系统 { usecase 借阅图书 as UC1 usecase 归还图书 as UC2 usecase 身份验证 as UC3 usecase 冻结借阅权限 as UC4 } R -- UC1 R -- UC2 UC1 .. UC3 : include UC2 .. UC3 : include UC4 .. UC1 : extend enduml这段代码生成的图关系一目了然。注意left to right direction让布局横向展开比默认的纵向布局更适合用例图。..是虚线箭头include和extend写在冒号后面作为标签规范又清晰。5. 画完之后的自查清单与常见返工点图画完不等于画对。我每次交付用例图之前会过一遍自查清单这里分享出来你可以直接拿去用。5.1 命名层面的自查每个用例是不是动宾结构图书借阅不如借阅图书用户管理这种名词化的要警惕它可能藏着好几个用例。参与者命名是不是角色名而不是人名张三不行读者可以。边界框命名是不是体现了系统定位系统太泛图书借阅管理系统更清楚。有没有出现技术术语用例图上不该出现数据库接口缓存这类词出现了说明你在画实现而不是画需求。5.2 关系层面的自查每条包含关系的被包含用例是不是真的被多个基用例共享只被一个用例包含的没必要抽出来。每条扩展关系的扩展用例是不是真的可选如果每次都会执行那它应该是包含或者直接合并进基用例。泛化关系是不是有必要子用例有没有独立的参与者或触发条件没有的话考虑降级为普通用例。关联线有没有多余的箭头箭头只表示发起方向不表示数据流。5.3 图面层面的自查线条有没有大量交叉有的话调整布局。用例椭圆大小是否一致不一致会显得不专业。参与者是否都在边界框外有一个跑进框里就是硬伤。图的整体阅读方向是否清晰从左到右或者从上到下别让人找不到入口。我踩过最典型的一个返工点是把包含关系画反了箭头。包含关系的箭头指向被包含的用例我一开始总记成指向基用例结果图上的语义完全反了——变成了身份验证包含借阅图书逻辑上说不通。后来我记了个笨办法包含的含字想象成把东西含进去箭头指向被含进去的那个。扩展的扩字想象成从主体扩出来箭头指向主体。这么记虽然土但再没画反过。另一个返工点是参与者重复。同一个外部系统在两张子图里用了不同的名字一张叫支付平台一张叫支付网关评审的时候被指出来显得很不严谨。后来我养成了习惯所有参与者先在文档里统一定义一次画图时直接复制粘贴名字不手打。6. 用例图在整个需求链路里的位置最后聊点图之外的东西。用例图画得好不好很大程度上取决于你对它在整个需求链路里位置的理解。它不是孤立的交付物而是承上启下的枢纽。往上它承接的是需求来源——用户访谈、业务调研、竞品分析。这些零散的信息经过你的梳理变成参与者和用例图就是梳理结果的呈现。所以画图之前的信息收集做得越扎实图画起来越顺。我一般会要求先有一份参与者-目标清单清单确认了再动笔返工率能降一大半。往下它支撑的是用例规约和测试用例。每个用例椭圆都应该对应一份用例规约规约里写清楚前置条件、基本流、备选流、后置条件。测试同学拿到用例图能快速圈出测试范围拿到规约能设计具体测试步骤。如果用例图上的用例和规约对不上号说明中间漏了东西得回去补。还有一个容易被忽略的用途用例图是跟非技术方沟通的好工具。给业务方看类图、时序图他们一头雾水看用例图小人加椭圆基本能看懂谁能干什么。所以图上的命名要尽量用业务语言别用技术黑话。这一点在评审会上特别重要图是给所有人看的不是给画图的人自己看的。我在实际项目里的体会是用例图的价值不在于画得多漂亮而在于画的过程中逼着团队把谁用、用来干嘛、边界在哪这三件事说清楚。很多时候图画到一半大家发现某个参与者的目标定义有分歧当场就对齐了这比图本身有价值得多。所以别把画图当成任务把它当成一次结构化的需求讨论心态会完全不一样。如果你刚开始学建议找一个自己熟悉的场景——比如学校选课、外卖下单、图书馆借书——从列参与者清单开始完整走一遍流程。画完第一版别急着交放一天再看你会发现一堆可以优化的地方。画到第三张图的时候那些关系判断就会变成肌肉记忆不用再对着口诀查了。