资讯动态

UML用例图:include包含与extend扩展的核心区别与应用

发布时间:2026/9/11 9:43:00 来源:尧图企业网站定制
1. 从用例图说起包含include与扩展extend各自的定位1.1 为什么这个老问题一直有人问带过的几个项目组里每次评审用例图包含include和扩展extend的关系总会被讨论半天。很多同学画用例图时对这两个关系心里没底要么全画成箭头要么全画成虚线评审时被问一句“这个地方为什么用扩展不用包含”就卡壳。这其实不是个例我见过不少工作三五年的开发画用例图时依然靠“感觉”来选关系。先解释一下背景。UML用例图是需求分析阶段最常用的图之一核心价值在于把“谁参与者能用系统做什么用例”以及“用例之间有什么关系”表达清楚。最近总有人问“AI时代还需要学UML吗”我的看法是AI再强也只能帮你把已经想清楚的业务逻辑加速落地没法替你把“边界划在哪、哪些步骤公共、哪些场景可选”想明白。用例图逼着你做这件事所以它依然是系统分析的基本功而include和extend正是基本功里最容易混淆的一环。1.2 包含关系include到底在表达什么include关系的中文含义是“包含”UML规范里的定义是一个用例基础用例在执行过程中总是会在某个点去调用另一个用例被包含用例的行为。被包含用例通常是一个公共片段被多个用例共享。我习惯用一个类比来理解include把用例图看成做菜的流程无论是做红烧肉还是清蒸鱼都需要“切姜片”这一步。只要有做菜这个主流程“切姜片”就是必须执行的不会因为这次做的菜不同就省略。“切姜片”就是被包含用例而具体做哪道菜则是基础用例。绘制时include关系用一条虚线箭头箭头上标注 方向从基础用例指向被包含用例。很多初学者会画反记住这个方向非常关键。在语义上include意味着每次执行基础用例都必须执行被包含用例它是主流程的一部分不是可选项。举一个常见的电商场景“提交订单”这个用例必须经过“身份校验”所以“提交订单”指向“身份校验”并标注 。同样“申请售后”也需要确认用户身份它也要包含“身份校验”。这样“身份校验”就被抽成公共用例两个基础用例都能复用后续如果要把身份校验升级为“双因素认证”只需要修改被包含用例本身所有包含它的用例自动生效。include关系的设计初衷是消除用例间的重复描述。假如没有include同一个“身份校验”流程要在“提交订单”“申请售后”“查看订单列表”等每个用例里重复写一遍用例图会变得极其臃肿需求文档也会出现大量冗余内容。抽成include后公共逻辑被统一收纳图面干净维护成本降低。1.3 扩展关系extend到底在表达什么extend关系的中文含义是“扩展”它表达的语义与include刚好相反基础用例在执行到某个扩展点extension point时在特定条件满足的情况下才会执行扩展用例里的行为。扩展用例不是主流程的必经环节而是“如果满足某个条件才会插入的一段逻辑”。还是用做菜打比方做菜这个主流程本身是完整的但“如果客人过敏就替换食材”这一步并不是每次做菜都会触发。这个“替换食材”的逻辑就是扩展用例它必须指向“做菜”这个基础用例表示它附着在某个可选条件下只有条件命中才执行。绘制时extend关系同样用虚线箭头箭头上标注 但方向与include相反是从扩展用例指向基础用例。这一点最初也让我困惑过为什么扩展用例要指向基础用例而不是反过来原因在于这种设计保证基础用例完全不感知扩展用例的存在。基础用例可以独立完成不管外面有没有扩展用例它都照着主流程执行扩展用例像是挂在主流程旁边的“插件”观察并等待自己的触发条件。拿订单结算举例“订单结算”这个用例本身是完整的但“如果用户选择了优惠券则执行优惠券抵扣”这明显是一个可选分支所以“优惠券抵扣”指向“订单结算”并标注 。如果用户没有选优惠券基础用例照常运行完全不受影响。2. 核心区别对比四个维度看清include和extend2.1 触发方式必选还是可选include和extend最直观的差别就在这里。include是“无条件执行”只要基础用例一启动被包含用例必然跟着执行。可以把它理解成一段函数体里的固定调用提交订单步骤 1. 校验用户身份 2. 校验商品库存 3. 生成订单数据 4. 返回下单结果“校验用户身份”和“校验商品库存”是订单提交主流程里拔不掉的步骤少任何一步订单都没法正常提交。这些步骤就适合建模成include。extend则对应“条件触发”。用类似伪代码的方式表达是这样的订单结算步骤 1. 计算商品总价 2. 若用户选择了优惠券则执行优惠券抵扣 3. 生成应付金额第2步带了一个“若”它就是典型的扩展点。用户不选优惠券结算流程照样能走完只有当条件成立时扩展逻辑才被插入。这个“条件是否成立”是判断include和extend的第一道分水岭无条件执行的走include有条件触发的走extend。这里有个容易踩的坑有人觉得“这个扩展用例我执行频率很高大概80%的场景都会触发那是不是应该用include”答案是否定的。触发概率高不等于无条件只要还存在“不触发”的合法业务场景语义上就依然是extend。比如“新用户首单立减”虽然平台希望大量用户都用它但用户完全可以放弃这个优惠直接结算所以它只能建模成extend。2.2 依赖方向谁依赖谁include和extend在依赖方向上完全相反这个点理解了很多模糊就消失了。在include关系中基础用例依赖被包含用例。换句话说被包含用例是基础用例能够完成的前置条件或者组成部分。比如“航班订票”这个用例include“查询航班”“查询航班”缺了“航班订票”就没法继续。这种依赖关系非常强是一级一级往下走的。在extend关系中扩展用例依赖基础用例而基础用例完全不依赖扩展用例。扩展用例通过基础用例暴露出的扩展点来插入自身行为但它不能改变基础用例的主流程。用策略模式来类比理解主流程定义好算法骨架具体策略在特定条件下替换或者插入但主流程本身不感知策略的存在。扩展用例如果不稳定、想加一个或多个新扩展基础用例根本不需要改动这正好符合面向对象里的开闭原则。我画图时经常用一句口头禅来验证**去掉这个关系基础用例还能独立完成业务吗**如果答案是“能”那它大概率是extend如果答案是“不能缺了它主流程就走不下去”那它更可能是include。这个判断方法我用了很多年准确率很高。2.3 语义场景公共逻辑还是条件分支include天然适合表达“多个用例之间共享的公共逻辑”extend天然适合表达“围绕某个主流程展开的可选分支或业务变体”。把这层语义搞明白看图就能一眼判断画得对不对。下面是我整理的常见场景对照表业务场景举例适合的关系判断理由多个用例都需要“登录校验”include公共前置步骤缺了就无法执行“订单结算”时有用户主动选择“使用优惠券”extend可选分支不选也能结算多个用例都需要“写入操作日志”include公共步骤每个用例都会做“下单支付”时触发“余额不足提示”extend仅在余额不足这个特定条件下触发“导出报表”需要“数据权限检查”include不检查权限就没法导出属于前置条件“下载文件”后用户可“申请开票”extend不是每个下载都会开票用户主动发起把这些案例对比着看会发现include解决的是“重复”问题extend解决的是“变化”问题。如果你的图里出现了同一个片段被三四个用例重复引用那应该考虑抽include如果你发现某个主流程旁边挂了一大堆“如果...就...”的分支那应该考虑用extend把这些分支剥离开。特别提醒一点extend并不代表扩展用例不重要。有些扩展用例承载着核心业务价值比如支付环节里的“风控校验”“清结算”在特定场景下缺一不可只是它们不总是被触发。用例图里用extend表达的是它在运行链路中的位置而不是它对业务的重要程度。2.4 图示呈现与工具操作UML图里include和extend的绘制方式都是虚线箭头加构造型差别集中在两处箭头方向和标签名。include基础用例 —— 被包含用例标签 extend扩展用例 —— 基础用例标签 在Enterprise Architect中我先连接两个用例然后在关系类型里选择Include或者Extend。这个工具会按UML规范自动调整箭头方向但我见过不少新手上手时把方向设反读图的人自然会理解反。所以每次画完我都会再核一遍箭头是不是从基础用例指向被包含用例include或者从扩展用例指向基础用例extend。PlantUML写起来更直观arrow的方向就是关系的方向例如startuml left to right direction actor 用户 rectangle 订单模块 { usecase 提交订单 as UC1 usecase 身份校验 as UC2 UC1 -- UC2 : include usecase 使用优惠券 as UC3 UC3 -- UC1 : extend } enduml这段代码里“提交订单”指向“身份校验”是include“使用优惠券”指向“提交订单”是extend。生成图之后一眼就能看出两个关系的方向差异。用工具的时候别只追求“画得出来”一定要能解释“为什么箭头是这个方向”这才是掌握include和extend的关键。3. 实操案例用“订单结算”场景把两种关系落地3.1 需求设定与用例清单理论说了一堆还是要落到具体场景里过一遍。我选了电商订单结算是为了贴近日常这个案例我已经在不同项目里用过好多次。假设我们要为某个电商系统画订单相关的用例图参与者包括“注册用户”和“支付网关”。需求可以梳理成以下用例提交订单订单结算身份校验库存锁定金额计算优惠券抵扣申请发票余额不足提醒这里的“提交订单”和“订单结算”是核心主流程用例“身份校验”“库存锁定”“金额计算”是主流程里抽取出来的公共步骤最后的“优惠券抵扣”“申请发票”“余额不足提醒”则明显具备可选性和条件性。颗粒度先定成这样才能把关系画得有价值如果颗粒度太粗一个用例装下所有事后面所有关系都无从谈起。3.2 如何给“身份校验”建模成include“身份校验”应该画成include原因有三个第一它是多个用例共同的必选步骤。提交订单要校验身份订单结算也要校验身份甚至查看订单详情可能也要校验身份。这些用例都必须在身份合法的基础上才能继续不校验身份就往下走业务上是不可接受的。第二它不依赖具体业务上下文。不管是提交订单还是结算身份校验的逻辑都一样抽出来不会损失信息反而让每个基础用例的描述都变得更简洁。第三它具备独立修改的价值。如果后续公司要求强实名认证我只要修改“身份校验”这一个被包含用例所有包含它的用例都自动升级不需要到每个用例里去逐一改动。画图时我用给“提交订单”和“订单结算”分别画一条指向“身份校验”的虚线箭头标注 。这样整张图的主流程链路就很清楚了先校验身份再进入各自的业务逻辑。业务人员看这张图能直观理解“身份校验是全局公共能力”不会误以为只有提交订单时才校验。同样我给“订单结算”include“金额计算”给“提交订单”include“库存锁定”逻辑都是同一个套路。只是注意一点不要让被包含用例堆积得太多如果一张图里同一个include对象连着六七个用例图面会变成“蜘蛛网”这时候要考虑按子系统拆分视图。3.3 如何给“优惠券抵扣”建模成extend“优惠券抵扣”适合画成extend核心原因在于它不是结算流程的必经环节。一个用户可以没有优惠券也可以有券但选择不用结算流程都能继续推进并完成。这完全符合extend的“条件触发”语义。我在图里这样处理“订单结算”是基础用例在它旁边画一个“优惠券抵扣”用例虚线箭头从“优惠券抵扣”指向“订单结算”标注 。同时在扩展箭头附近写明触发条件和扩展点比如“条件用户选择优惠券扩展点金额计算完成后”。这样需求人员看到图立刻知道这是在“金额计算完成之后、生成支付单之前”插入的一个可选步骤。“申请发票”也画成extend挂在“订单结算”或“提交订单”旁边。它不是每次下单都会做是用户下单后、支付前或者支付后主动发起的一个动作。比把“申请发票”硬塞进主流程里用extend更符合业务直觉。“余额不足提醒”同样画成extend挂在“订单结算”基础上触发条件是“支付时账户余额不足”。这是一种典型的异常分支但它在业务上完全可预期、可描述属于业务规则层面的分支所以适合建模。至于数据库连接失败、网络超时这类系统异常我不会建模成extend它们更适合在非功能性需求或者差错处理文档里描述。3.4 从这张图看可维护性收益把include和extend都用上去之后这张订单结算用例图会变得非常清晰。主流程用例是“提交订单”和“订单结算”它们共享“身份校验”按步骤依赖“库存锁定”和“金额计算”旁边挂上“优惠券抵扣”“申请发票”“余额不足提醒”这三个扩展用例。你一眼就能说出系统里哪些是“必须做的事”哪些是“看情况做的事”。对比另一种画法不抽取公共逻辑把所有步骤平铺在每个用例里图会臃肿需求文档里到处是重复描述不用extend把所有可选分支写进主流程事件流图会变成一条超长的主线每个分支都往里塞后续哪怕只改一个“优惠券规则”也要理解整条链路的上下文。用include和extend建模之后业务变更的影响范围被隔离了改优惠券规则只动“优惠券抵扣”这个扩展用例改库存逻辑只动“库存锁定”这个被包含用例改身份认证只动“身份校验”。这就是用例图的价值——它不是画给评审看的装饰图是真正让需求和代码一样具备可维护性的设计产物。4. 常见误区与挑选建议避坑实录4.1 误区一只要多个用例都用同一段流程就无脑加include这是我在评审时遇到最多的错误。很多同学一看到“提取公共逻辑”这句话就恨不得把一切重复内容都抽成用例结果出现“登录校验”“加载列表”“发送通知”满天飞的情况。include确实适合处理公共逻辑但前提是被抽取出来的内容对业务人员来说是一个有意义、可独立描述的行为。举个例子多个用例都要读取系统当前时间这确实重复但它只是一个技术细节不具备业务完整性把它建模成一个include用例会让用例图偏离需求视图的本质。用例图是需求视图不是实现视图。某个片段如果只是几步简单操作用辅助函数、类方法或者服务层封装就足够了没必要上升到用例层面。判断标准很简单**把这段逻辑抽出来后你能不能不在代码里看到它而是仅仅看用例图就给业务方讲清楚它是什么**不能就别抽。4.2 误区二把所有异常情况都画成extend与前一误区相反另一类同学特别喜欢把异常处理画成extend觉得“只要不是主流程都算扩展”。这样做的直接后果是图上一堆“系统超时”“数据库连接失败”“缓存未命中”这类扩展用例把真正有业务价值的可选分支给淹没了。要澄清一点extend针对的是业务规则层面的条件分支而不是所有非主流程行为。“余额不足提醒”是业务上的可预期分支因为它能由用户的操作和账户状态触发业务人员看得懂“数据库连接失败”则是系统内部的技术异常业务流程图上画它既不能帮助业务人员理解需求也会让图失真。我通常只把“用户可感知、且与业务决策相关”的分支建模成extend。如果你的扩展用例是“重试队列处理”“降级开关”这类技术手段我建议把它放到系统设计文档、活动图或时序图里表达别硬塞进用例图。4.3 误区三为了区分关系反而把图画复杂还有一种情况是开发者对include和extend的理论倒背如流画图时却陷入“这个该用include吧不对那个人说是extend”的纠结最后一张图里又include又extend到处是虚线箭头整体比都市立交桥还复杂。用例图的最佳实践是每一张图聚焦一个业务模块用例数量尽量控制在9到12个以内。如果include和extend加起来超过七八个通常说明业务边界没有理清楚或者你在试图用一张图表达多个场景。这时候先退一步重新回答几个问题这个模块的主流程到底有几个用例哪些步骤是多个用例共享的、不带条件、不能省略的哪些分支是真正常见的业务变体回答完之后再动手画。另外如果某些可选流程确实很多可以考虑把它们拆到另外一张用例图里比如“订单结算”有一张主图“结算扩展功能”再单独一张图这比挤在一张图里更容易维护。4.4 选择建议速查表最后把最常用的判断维度整理成一张速查表方便你下次画图时对照使用。判断维度倾向选择include倾向选择extend触发条件无条件基础用例必做有条件满足条件才做复用情况被多个基础用例共享通常挂在单个基础用例上基础用例独立性去掉它主流程走不完去掉它主流程依然完整修改影响修改被包含用例影响所有包含者新增扩展不影响基础用例表达重点公共逻辑、前置条件可选分支、业务变体、异常分支再给一个我自己的决策口诀**先找主流程再抽公共步骤最后挂可选分支没有公共不include没有条件不extend。**这段话每次画图前我都会默念一遍它能帮我快速从需求描述里识别出用例之间的正确关系。我个人在实际项目里的体会是评审用例图时尽量不纠结“这个名字到底叫include还是extend”而是换成业务提问方式如果我把这个用例从系统里拿掉用户需要办理的业务还能不能完成能完成它就是扩展不能完成它就是包含。用这个视角去看绝大多数争论都可以很快平息。希望这套判断方法对你也有用。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价