
5.2.4 用例之间的关系
为了减少模型维护的工作量,保证用例模型的可维护性和一致性,可以在用例之间抽象出包含(Include)、扩展(Extend)和泛化(Generalization)这几种关系。这几种关系都是从现有的用例中抽取出公共信息,再通过不同的方法来重用这部分的公共信息。
1.包含
包含关系是指用例可以简单地包含其他用例具有的行为,并把它所包含的其他用例的行为作为自身行为的一部分。在UML中,包含关系是通过带箭头的虚线段加<<include>>字样来表示,箭头由基础用例(Base)指向被包含用例(Inclusion),如图5-13所示。包含关系代表着基础用例会用到被包含用例,就是将被包含用例的事件流插入到基础用例的事件流中。
需要注意的是,包含(Include)关系是UML 1.3中的表述,在UML 1.1中,同等语义的关系被表述为使用(Uses),如图5-14所示。

图5-13 包含关系

图5-14 使用关系
在处理包含关系时,具体的做法就是把几个用例的公共部分单独地抽象出来成为一个新的用例。主要有两种情况需要用到包含关系:
- 多个用例用到同一段的行为,可以把这段公共的行为单独抽象成为一个用例,然后让其他用例来包含这一用例。
- 某一个用例的功能过多、事件流过于复杂时,就可以把某一段事件流抽象成为一个被包含的用例,以达到简化描述的目的。
下面来看一个具体的例子,有一个资源网站,维护人员要对网站的资源进行维护,包括添加资源、修改资源和删除资源。其中,在添加资源和修改资源后,都要对新添加的资源和修改的资源进行预览,以检查添加和修改操作完成是否正确。用例图如图5-15所示。

图5-15 包含关系示例
这个例子就是把添加资源和修改资源都会用到的一段行为抽象出来,成为一个新的用例—预览资源。而原有的添加资源和修改资源这两个用例都会包含这个新抽象出来的资源。如果以后需要对资源预览进行修改,则不会影响到添加资源和修改资源这两个用例。并且由于是一个用例,就不会发生同一段行为在不同用例中描述不一致的情况。通过这个例子可以看出包含关系的两个优点:
- 提高了用例模型的可维护性,当需要对公共需求进行修改时,只需要修改一个用例而不必修改所有与之有关的用例。
- 不但可以避免在多个用例中重复地描述同一段行为,还可以避免在多个用例中对同一段行为的描述不一致。
2.扩展
在一定条件下,把新的行为加入到已有的用例中,获得的新用例叫作扩展用例(Extension),原有的用例叫作基础用例(Base),从扩展用例到基础用例的关系就是扩展关系。一个基础用例可以拥有一个或者多个扩展用例,这些扩展用例可以一起使用。在UML中,扩展关系是通过带箭头的虚线段加<<extend>>字样来表示,箭头指向基础用例,如图5-16所示。

图5-16 扩展关系
扩展关系和包含关系有很大的不同:
- 在扩展关系中,基础用例提供了一个或者多个插入点,扩展用例为这些插入点提供了需要插入的行为。而在包含关系中,插入点只能有一个。
- 基础用例的执行并不一定会涉及扩展用例,扩展用例只有在满足一定条件下才会被执行。而在包含关系中,当基础用例执行后,被包含用例是一定会被执行的。
- 即使没有扩展用例,扩展关系中的基础用例本身就是完整的。而对于包含关系而言,基础用例在没有被包含用例的情况下就是不完整的存在。
让我们来看一个具体的例子,如图5-17所示为图书馆管理系统用例图的部分内容。在本用例中,基础用例是“还书”,扩展用例是“缴纳罚金”。在一切顺利的情况下,只需要执行“还书”用例即可。但是,如果借书超期或者书有破损,借书用户就要缴纳一定的罚金。这时就不能执行用例的常规动作,如果去修改“还书”用例,势必增加系统的复杂性。这时就可以在基础用例“还书”中增加插入点,这样当出现超期或破损的情况时,就执行扩展用例“缴纳罚金”。
扩展关系往往被用来处理异常或者构建灵活的系统框架。使用扩展关系可以降低系统的复杂度,有利于系统的扩展,提高系统的性能。扩展关系还可以用于处理基础用例中那些不易描述的问题,使系统显得更加清晰和易于理解。

图5-17 扩展关系示例
3.泛化
用例的泛化指的是一个父用例可以被特化成多个子用例(从一般到特殊),而父用例和子用例之间的关系就是泛化关系。在用例的泛化关系中,子用例继承了父用例所有的结构、行为和关系,子用例是父用例的一种特殊形式。此外,子用例还可以添加、覆盖、改变继承的行为。在UML中,用例的泛化关系通过一个三角箭头从子用例指向父用例来表示,如图5-18所示。

图5-18 泛化关系
当我们发现系统中有两个或者多个用例在结构、行为和目的方面存在共性时,就可以使用泛化关系。这时,可以用一个新的(通常也是抽象的)用例来描述这些公共部分,这个新的用例就是父用例。如图5-19所示,用例图为飞机订票系统预定机票有两种方式,一种是通过电话预定,一种是通过网上预定。在这里,电话订票和网上订票都是订票的一种特殊方式,因此“订票”为父用例,“电话订票”和“网上订票”为子用例。

图5-19 泛化关系示例
虽然用例泛化关系和包含关系都可以用来复用多个用例中的公共行为,但是它们还是有很大区别的。在用例的泛化关系中,所有的子用例都有相似的目的和结构,注意它们是整体上的相似。而用例的包含关系中,基础用例在目的上可以完全不同,但是它们都有一段相似的行为,它们的相似是部分相似而不是整体相似。用例的泛化关系类似于面向对象中的继承,它把多个子用例中的共性抽象成一个父用例,子用例在继承父用例的基础上可以进行修改。但是,子用例和子用例之间又是相互独立的,任何一个子用例的执行不受其他子用例的影响。而用例的包含关系是把多个基础用例中的共性抽象为一个被包含用例,可以说被包含用例就是基础用例中的一部分,基础用例的执行必然引起被包含用例的执行。