用例图中,“include”和“extend”的区别是什么?

475
用例图中,includeextend有什么区别?

我不如Scott Ambler更擅长解释如何在用例模型中使用它们进行重用以及它们的区别。因此,我建议阅读《在用例模型中重用:<<扩展>>、<<包含>>和继承》(http://www.agilemodeling.com/essays/useCaseReuse.htm),而不是对他进行改述。 - Pascal Thivent
40
@closers:这确实是一个有效的问题。 - H H
115
简而言之,include = 必选,extend = 可选。 - Thein
2
@Megamind,“extend = Optional”并不完全正确......请看这个例子[链接](http://agilemodeling.com/essays/useCaseReuse.htm) - Shanaka Jayalath
1
每当一个用例需要另一个用例的行为时,您就会使用包含依赖项。引入一个封装了多个用例中出现的类似逻辑的新用例是非常常见的。- 详见:http://www.agilemodeling.com/essays/useCaseReuse.htm#Include - Noman Chali
20个回答

328

Extend用于向另一个一级用例添加步骤。

例如,想象一下“取款”是自动柜员机(ATM)的一个用例。 "收费"将扩展"取款"并描述当ATM用户没有在ATM所属机构开户时实例化的条件“扩展点”。请注意,基本的“取款”用例可以独立存在,没有这个扩展。

Include用于提取在多个用例中重复的用例片段。被包含的用例不能独立存在,原始用例没有包含的用例是不完整的。应该节俭地使用,并且仅在重复是显著的且是有意设计的情况下使用(而不是巧合的情况)。

例如,每个ATM用例开始时发生的事件流程(用户插入ATM卡,输入密码,显示主菜单)将成为包含的良好候选。


3
Include 是用来提取在多个用例中重复的用例片段的,那么在这些步骤中提取了什么呢:输入他们的 ATM 卡、输入他们的 PIN 码并显示主菜单。谢谢。 - Blaze Tama
5
我不同意将“登录”步骤作为一个“包含(include)”的好候选者。这些步骤本身就构成了一个用例,应该通过后置条件->前置条件与其他用例相链接。 - Bruno Brant
41
@Bruno - 没有人会登陆ATM机并只是走开感到高兴。具体的使用案例必须为操作者提供独立的价值,否则它们只是功能分解中的函数。 - Doug Knesek
1
如何将“评估费用”作为UC?这是一个场景中的条件流程。它根本不是增值,而是完全相反的情况。 - qwerty_so
1
@ThomasKilian - 根据UML 2.5的描述,“扩展”应该在需要添加一些额外行为(可能是有条件的)到一个或多个用例中定义的行为时使用。而且,“扩展用例通常定义的行为本身可能并不具有意义。” 具体的用例必须为参与者提供价值,而不是扩展用例。 - Doug Knesek
显示剩余6条评论

139

这可能会引发争议,但“包含(includes)总是,扩展(extends)有时”是一个非常常见的误解,几乎已经成为事实上的含义。以下是一种正确的方法(在我的观点下,并经过了Jacobson、Fowler、Larmen和其他10个参考文献的核对)。

关系是依赖

理解使用案例之间的包含和扩展关系的关键在于意识到,与UML的其余部分相同,使用案例之间的虚线箭头表示依赖关系。我将使用“基础”、“包含”和“扩展”这些术语来指代使用案例角色。

包含(include)

基础使用案例依赖于包含的使用案例;如果没有它/它们,基础使用案例就不完整,因为包含的使用案例代表可能始终或有时发生的交互子序列。(这与一个流行的误解相矛盾,即您的使用案例中所提供的主要情景永远发生在主流程中,有时发生在备选流程中,这只取决于您选择的主要情景;使用案例可以很容易地重构为表示不同的流程作为主要情景,这应该无关紧要。)

在单向依赖的最佳实践中,基础使用案例了解(并引用)包含的使用案例,但包含的使用案例不应“知道”基础使用案例。这就是为什么包含的使用案例可以是:a)它们自己的基础使用案例;b)多个基础使用案例共享的原因。

扩展(extend)

扩展的使用案例依赖于基础使用案例;它实际上扩展了基础使用案例所描述的行为。基础使用案例应该是一个完全功能的使用案例(当然包括了包含的使用案例),没有扩展使用案例的额外功能也可以正常运行。

扩展使用案例可用于几种情况:

  1. 基础使用案例表示项目的“必须拥有”功能,而扩展使用案例表示可选的(应该/可能/想要的)行为。这就是“可选”一词相关的地方——可选是指是否构建/交付,而不是是否有时作为基础使用案例序列的一部分运行。
  • 在第一阶段,你可以交付满足那时要求的基本用例,第二阶段将增加由扩展用例描述的附加功能。这可能包含在第二阶段之后总是或有时执行的序列(与普遍误解相反)。
  • 它可用于提取基本用例的子序列,特别是当它们代表具有自己的备选流程的“异常”复杂行为时。
  • 需要考虑的一个重要方面是,扩展用例可以在基本用例的流程中的多个位置“插入”行为,而不仅仅是像包含用例一样在单个位置上。因此,几乎不可能使用扩展用例来扩展多个基本用例。

    至于依赖性,扩展用例依赖于基本用例,并且再次是单向依赖,即基本用例在序列中不需要引用扩展用例。这并不意味着你不能演示扩展点或将对扩展用例的交叉引用添加到模板的其他位置,但是基本用例必须能够在没有扩展用例的情况下工作。

    总结

    我希望我已经表明,“包括始终,扩展有时”的普遍误解是错误的或者最多只是简单化了。如果你考虑有关箭头方向性的所有问题,这个版本实际上更有意义-在正确的模型中,它只是依赖关系,如果你重构用例内容,它不会潜在地改变。


    4
    如果您能为该主张添加一些参考资料,那将非常好。 - mibollma
    2
    抱歉,按照这种方式创建 UML 图表是没有必要的,因为软件会经过添加新功能的迭代过程,这些功能始终会在最终状态中需要。这样做只会让事情变得更加混乱和复杂。我更喜欢 Doug Knesek 的做法,更容易理解和操作,同时也不会造成任何不必要的混乱或复杂性。 - BigMac66
    2
    尽管你有理地质疑“包含关系总是”和“继承关系有时”的用例实例相关性,但你选择利用“继承”语义来支持递增式交付,可能会让其他人认为“继承”与递增式交付有关。 - Doug Knesek
    1
    这是UML 2.0规范文档。它清楚地定义了用例中包含和扩展的含义:“16.3.3”位于https://www.omg.org/spec/UML/2.0/Superstructure/PDF。 - Underlines
    下划线,这是一份大型文档,为该响应添加页码会更有用。 - Andrew S

    109

    我经常使用这个来记住这两个要点:

    我的使用场景: 我要去城市。

    包括 -> 驾驶汽车

    扩展 -> 加油

    "加油"可能并不总是必要的,但根据汽车中还剩多少汽油可以选择性地进行。"驾驶汽车"是前提条件,因此我将其包含在内。


    3
    但是,“加满汽油”实际上扩展了“去城市”,而不是相反的,对吗? - Petr Hudeček
    3
    我认为这取决于观点,Petr。在我看来,“加满油”实际上也可以延长汽车的行驶时间。 - Daniel Perník
    5
    去城市和加油听起来像是两件完全没有关联的事情,可能只是巧合。 - OdraEncoded
    4
    @OdraEncoded 是正确的。加油不取决于去城市与否。 - nonybrighto

    77

    用例用于记录行为,例如回答这个问题。

    answer the question use case

    如果一个行为是在另一个行为的基础上增加的,但不一定是该行为的一部分,则称之为扩展,例如研究答案。

    还要注意,如果您不打算回答这个问题,研究答案就没有意义。

    research the answer extend

    如果一个行为是包含在另一个行为中的一部分,则称为包含,例如登录到Stack Exchange。

    login to stack exchange include

    需要澄清的是,这个图示仅在您想在Stack Overflow这里回答问题时才是真实的。:)

    这些是来自UML 2.5页面671-672的技术定义。

    我强调了我认为重要的点。

    扩展

    扩展是从扩展用例(扩展)到被扩展用例(被扩展案例)的关系,指定了在何时以及如何将定义在扩展用例中的行为插入到被扩展用例中定义的行为中。 扩展发生在被扩展用例中定义的一个或多个特定扩展点上。

    当需要添加一些额外的行为可能是有条件的,Extend 被设计用于此目的。

    扩展用例是独立于被扩展的用例定义的,并且在不考虑被扩展的用例时也具有意义。另一方面,扩展用例通常定义了在本身情况下可能并没有意义的行为。 相反,扩展用例定义了一组模块化的行为增量,在特定条件下增强了被扩展用例的执行。

    ...

    包含

    包含是两个用例之间的DirectedRelationship,表示包含使用案例(附加部分)被插入到包含使用案例(包含用例)的行为中。它也是一种NamedElement,因此在其所属的UseCase(包含用例)上下文中可以有一个名称。包含用例可能依赖于执行包含用例产生的更改。为了完整描述包含用例的行为,必须可用包含用例的行为。

    包含关系旨在用于两个或多个使用案例的行为存在共同部分的情况。然后将这个公共部分提取到一个单独的使用案例中,以便所有具有这个公共部分的基础使用案例都可以包含它。由于包含关系的主要用途是重用公共部分,所以留在基础使用案例中的内容通常本身并不完整,而是依赖于包含部分才能有意义。这反映在关系的方向上,表示基础使用案例依赖于附加部分但反之则不然。

    ...


    我认为这个登录示例是错误的。这意味着每次需要回答问题时都必须登录,但如果您已经登录了,要执行其他用例怎么办? - Ahmed Alhallag

    74
    为了简化,对于include
    1. 当基础用例被执行时,所包含的用例会每次都被执行。
    2. 基础用例需要所包含的用例完成后才能被完成。
    一个典型的例子是:在登录和验证密码之间。
    登录---<< include >>--->验证密码
    为了使登录过程成功,“验证密码”也必须成功。
    对于extend
    1. 当基础用例被执行时,所扩展的用例只有时候会被执行。
    2. 只有在满足某些条件时才会发生扩展用例。
    一个典型的例子是:在登录和显示错误消息之间(仅在某些情况下发生)
    登录<---<< extend >>---显示错误消息
    只有在登录过程失败时,“显示错误消息”才会发生。

    很棒的例子! - Pavel Durov
    4
    “include”表示“需要”,而“extend”表示“可能”。 - testing_22
    好的描述,有问题的例子... “验证密码”和“显示错误信息”是使用案例吗?听起来更像是系统应该做的事情。(使用案例=某人使用系统的情况。)据我个人看法,“登录”甚至不是一个使用案例,而是系统强制用户为了正常工作而执行的烦人操作。 - Stefan Steinegger

    31

    我认为理解包含和扩展的意图非常重要:

    "包含关系旨在通过另一个用例建模并重用行为,而扩展关系旨在添加现有用例的部分以及建模可选系统服务" (Overgaard和Palmkvist,《用例:模式和蓝图》Addison-Wesley, 2004)。


    我认为这个意思是:

    包含 = 功能的重用(即包含的功能在系统中被使用或可能被使用)。因此,包含表示对另一个用例的依赖性。

    扩展 = 添加(而不是重用)功能和任何可选功能。因此,扩展可以表示两种情况:
    1、向用例添加新的特性/功能(可选或不可选)
    2、任何可选用例(现有的或不现有的)。

    总结:
    包含 = 功能的重用
    扩展 = 新的和/或可选的功能

    你最常见到扩展的第二种用法(即可选功能),因为如果功能不是可选的,那么大多数情况下它会内置到用例本身中,而不是作为扩展。至少这是我的经验。(Julian C指出,在项目进入第二阶段时,有时会看到扩展的第一种用法(即添加新功能)。


    23

    我认为MSDN在这里所解释的内容相当易于理解。

    包含 [5]

    包含用例调用或调用被包含的用例。包含用于显示用例如何分解为更小的步骤。被包含的用例位于箭头末端。

    扩展 [6]

    与此同时,扩展用例向扩展的用例添加目标和步骤。扩展仅在特定条件下运行。被扩展的用例位于箭头末端。

    enter image description here


    1
    你的回答至少应该引用要点,而不仅仅是添加一个链接。此外,你所参考的解释也并不好或准确。 - Geert Bellekens
    嗨@GeertBellekens,我已经添加了一些细节来解释包含和扩展之间的区别。在我看来,图表本身清楚地传达了差异,这就是图表的用途。 - Etic
    我很高兴你加了解释,但我仍然认为它们不是很好或精确。人们(尤其是那些为MSDN编写的人)应该停止为已经在标准中定义的东西发明自己的定义。 - Geert Bellekens

    15

    让我们更清晰地解释一下。每当我们想表达一个情况的存在取决于另一个情况的存在时,就使用include

    例子:

    用户只有在登录账户后才能在线购物。换句话说,在他登录账户之前,他不能进行任何购物。

    在素材上传之前,用户无法从网站上下载。因此,如果没有上传任何内容,我不能下载。

    你明白了吗?

    这是关于条件结果的。如果之前我没有做某事,那么我就不能做这件事

    至少,我认为上面关于笔记本电脑和保修的例子是最有说服力的!


    1
    关于 extends? - AlphaGoku

    9
    每当使用情况有前提条件时,应该使用 include。
    对于需要身份验证、最坏情况或可选的用例,则应使用 extend。
    例如:对于寻求入学、预约、订票等用例,您必须填写表格(注册或反馈表格)......这就是 include 的作用。
    例如:对于验证登录或登录帐户的用例,您的身份验证是必须的。还要考虑最坏情况,例如罚款后退还图书、未能预订和超过截止日期后支付账单......这就是 extend 的作用......
    在图表中不要过度使用 include 和 extend。
    保持简单傻瓜!

    8
    <include><extend> 都依赖于基类,但是<extend>是可选的,也就是说它是从基类派生出来的,但从用户的角度来看,它可以使用或者不使用。 <include> 被纳入到基类中,也就是说在您的用例中使用<include>是强制性的,否则会被视为不完整。
    例如:
    在 ATM 机构建中(根据用户的角度):
    1:取款、存款、查询账户属于<extend>,因为这取决于用户是否要取款、存款或查询,这些都是可选的。
    2:输入密码、插入卡片、拿出卡片等操作属于<include>,因为用户必须插入一张卡片并输入有效的密码进行验证。

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接