
在软件需求和系统建模领域,UML(统一建模语言)仍然是可视化系统行为的基石。在其最强大却经常被误解的功能中,包括«include» 和 «extend» 用例之间的关系。这些机制旨在减少重复, 管理变异性,以及增强模块化用例模型中。然而,它们的误用极为普遍——导致图表过于复杂,利益相关者困惑,以及对用户价值的关注丧失。

本文提供了一份全面、实用且有专家支持的指南用于理解、应用和避免«include»和«extend»的常见陷阱。我们将探讨它们的真实语义,进行对比分析,探讨它们为何陷入与DFD(功能分解)相同的陷阱,并提供现代最佳实践适用于2025–2026年团队——尤其是那些在敏捷、精益或混合环境中工作的团队。
定义:
«include» 关系表示一个必须的,始终执行被提取出来以在多个用例中复用的子流程。
始终执行:包含的用例在每次调用基础用例时都会运行。
没有它,基础用例就不完整:如果没有包含的行为,基础用例就无法实现其目的。
依赖方向:箭头指向从基础 → 包含.
独立意义:包含的用例通常单独没有意义——只有作为更大流程的一部分时才有意义。
类比:就像编程中的函数调用或子程序——对主程序至关重要。
“要执行登录,你必须验证用户.”
“要执行取现,您必须验证PIN码.”
这些是不可协商的步骤没有认证您无法登录。没有验证PIN码您无法取现。
当一个常见、复杂且可复用的行为出现在两个或更多用例中。
示例:
用户认证
记录审计日志
发送通知
验证输入格式
✅ 经验法则:仅当复用的行为是重要, 非平凡,并且出现在≥2–3用例中。
定义:
「扩展」关系定义了可选的、条件性的或变体的行为,它插入到一个特定的扩展点基础用例中。
条件执行:仅在特定条件下运行。
基础用例可独立完整:正常流程无需扩展即可运行。
依赖方向:箭头指向从扩展 → 基础(反向)。
独立意义:扩展用例通常几乎从不单独有意义——只有在上下文中才有意义在上下文中.
类比:就像一个钩子, 插件,或AOP(面向切面编程)建议——它在特定点添加行为。
“在执行 预订航班时,您可能会想要选择首选座位.”
“在执行 使用信用卡支付时,您可能会需要输入3D安全码.”
这些是可选的增强功能——并非核心流程所必需。
用于建模替代路径, 异常情况,或可选功能.
当用例具有变体行为基于条件(例如,用户角色、系统状态、偏好)时。
示例:
应用折扣(扩展下单)
申请退款(扩展处理付款)
生成PDF收据(扩展完成交易)
✅ 经验法则:谨慎使用«扩展»——仅用于有意义的变体具有明确的扩展点.
| 方面 | «包含» | «扩展» |
|---|---|---|
| 执行 | 总是 | 有时/有条件地 |
| 基础用例是否可独立完成? | ❌ 否——依赖于包含的内容 | ✅ 是——无需扩展即可完成 |
| 依赖方向 | 基础 → 包含 | 扩展 → 基础 |
| 箭头方向 | 指向被包含的用例 | 指向基础用例 |
| 主要目标 | 重用必需的、共享的步骤 | 处理可选/变体流程 |
| 类比 | 函数调用 / 子程序 | 钩子 / 插件 / AOP建议 |
| 具有独立意义吗? | 很少 | 几乎从不 |
| 最适合 | 可重用的、复杂的、横切关注点 | 条件性、可选性或替代性行为 |
就像DFD(数据流图)会受到功能分解陷阱,用例图也容易患上同样的致命疾病: 过度分解.
团队不断将流程拆分成越来越小的气泡。
图表分解为数十个微小的低级函数。
那个 原始目的——为用户提供价值——被遗忘了。
最终看起来像 伪代码 或 内部算法设计,而不是用户行为。
每一个微小步骤都变成了一个独立的用例:
输入用户名
输入密码
点击登录按钮
验证格式
显示错误信息
«包含»被应用 广泛地 以分解每一个操作。
结果:一个 深层层级 的用例层级(A → B → C → D…),没有明确的参与者目标。
图表变得 难以维护, 令人困惑,以及 对利益相关者毫无用处 对利益相关者。
❌ 红色警告: 如果你的用例图包含超过15到20个用例,或者如果大多数基础用例的步骤为2到4步你很可能陷入了陷阱。
| 陷阱 | 解释 | 如何避免 |
|---|---|---|
| 过度使用«include» | 将每个子步骤都视为可重用的用例。 | 仅在以下情况下使用«include»:重要, 可重用, 跨切面行为(例如:认证、日志记录)。 |
| 混淆箭头方向 | 将«include»箭头画反(基础 ← 包含)或«extend»箭头画正向。 | 记住:include = 基础 → 包含; extend = 扩展 → 基础. |
| 将«extend»用于替代流程 | 将替代流程建模为内部一个用例的«extend»,而不是使用文本形式的替代方案。 | 使用 文本替代流程 用于大多数变体。将«扩展»保留用于 真正的可选扩展. |
| 创建包含链 | A → B → C → D → E… | 避免过深的链。如果需要多个包含,考虑 重构为单一可重用用例. |
| 模糊的扩展点 | 在没有明确命名插入点的情况下添加«扩展»关系。 | 定义 明确的扩展点 (例如,“支付确认后”)在基础用例中。 |
| 图表杂乱 | 用例和关系过多 → 视觉干扰。 | 保持图表 简洁、聚焦且以参与者为中心。每个子系统使用多个图表。 |
| 利益相关者困惑 | 非技术利益相关者难以理解«包含/扩展»。 | 使用 文本场景或 用户故事地图 以提高清晰度。 |
| 设计层面的建模 | 建模内部架构(例如,“调用数据库”)而非用户目标。 | 专注于 角色价值——而非实现。 |
| 无休止的争论 | 团队争论“是使用include还是extend?”而不是编写用例场景。 | 使用实用的启发式方法和优先考虑清晰性而非形式化. |
需求工程的格局已经发生变化。敏捷、精益和以产品为导向的团队正越来越多地摆脱复杂的UML图,转而采用轻量级、以价值为导向方法。
❗ 在添加任何«include»或«extend»之前,请问这个问题:
“这个关系有助于用户理解目标吗?还是仅仅在分解系统?”
仅用于横切关注点出现在多个用例中。
示例:
验证用户
发送电子邮件通知
记录安全事件
应用业务规则
❌ 避免:
输入用户名,点击提交,验证电子邮件格式
而不是:
「扩展」:选择首选座位 → 预订航班
使用:
用例:预订航班
...
替代流程:
1. 用户选择「首选座位」选项。
2. 系统显示座位图。
3. 用户选择座位。
4. 系统应用座位偏好。
✅ 为什么?文本流程具有更易阅读, 更高的灵活性,以及更不易被误用.
每个参与者或子系统.
限制为5–10 个用例每个图。
使用包图或上下文图以展示高层结构。
如果你使用了10个以上的«包含»/«扩展»关系,请考虑用以下方式替代该图:
一个用户故事地图
一个基于场景的表格
一个简单的流程图并包含关键路径
🔄 现代趋势:许多敏捷团队完全避免使用用例图或仅在高层探索时使用.
将 «extend» 保留给可选的,非核心功能,例如:
是很少使用
是依赖上下文
是独立的与核心目标无关
✅ 示例:
处理付款(基础)
应用 3D 安全认证(扩展)——仅在银行要求时使用
❌ 避免:
输入卡号→验证卡片→处理付款(所有步骤都应属于同一个用例)
| 规则 | 指导 |
|---|---|
| 1. «include» = 必需 | 仅用于关键且可重用在至少两个用例中出现的步骤。 |
| 2. «extend» = 可选 | 仅在以下情况下使用条件性、变体或罕见行为。 |
| 3. 基础用例必须完整 | «extend»:基础用例可独立运行。«include»:基础用例缺少它则不完整。 |
| 4. 保持简洁 | 如果一个用例在«include»/«extend»之后的步骤少于4–6步,说明你过度分解了。 |
| 5. 优先考虑可读性 | 文字场景 > 复杂图示。 |
| 6. 避免链式结构 | 不要出现 A → B → C → D 的结构。应重构为一个可复用的用例。 |
| 7. 了解你的受众 | 利益相关者并不关心«include»箭头——他们关心的是价值. |
| 8. 问自己:“这是用户目标还是内部步骤?” | 如果它不是参与者的目标,很可能就不应包含在用例中。 |
«include» 和 «extend» 是强大的工具——而非僵化的规则。它们的设计目的是:
减少重复
管理变异性
提高可维护性
但就像DFD中的功能分解一样,它们一旦被过度使用,就会变成危险的武器当被过度使用时。真正的危险并非这些关系本身——而是忽视用户的目标.
🔥 记住:
用例不是技术流程。
它是一个关于用户想要实现的目标的故事——以及系统如何提供帮助。
如果不确定,问问自己:
“用户在不了解UML的情况下能否理解这一点?”
如果不能,就简化。
如果可以,你就走在正确的道路上。
UML 规范(OMG): www.omg.org/spec/UML
马丁·福勒 – 用例建模: 分析模式 & UML 精要
伊瓦尔·雅各布森 – 面向对象的优势:用例的基础性工作
敏捷建模(AM)作者:斯科特·W·安布勒
用户故事地图作者:杰夫·帕顿 – 复杂图表的现代替代方案
使用«include»进行强制重用,«extend»用于可选的变体——但仅在真正增加价值时才使用。否则,保持简单。
因为最终,目标并不是绘制完美的UML图——而是构建能为真实用户带来实际价值的系统。
📌 作者注(2025–2026):
随着团队转向以产品为中心, 以价值为导向,以及协作式开发,传统UML图的作用正在演变。«include»和«extend»仍然有用——但只有在克制、清晰且有目的性地使用时。让它们服务于用户,而非图本身。