在UML中,参与者和用例都可以被泛化或特化,它们在泛化或特化时遵循面向对象中泛化与特化的特性。
用例泛化在有多种方法实现用例目标时非常有用。例如在许多需求场景中都需要“付款”,而当下人们支付的方式多种多样,除了传统的“现金支付”方式外,还可以选择“刷卡支付”或者“扫码支付”。刷卡支付又包括“信用卡支付”和“借记卡支付”两种实现途径,而扫码支付则可以选择“支付宝支付”或“微信支付”。这些付款用例之间的关系可以采用图 1建模。
图 1 用例泛化建模
上图中用例之间通过泛化体现出它们之间存在的关系,而且在图中除叶子结点用例外,其余用例都是泛化用例,对泛化用例添加、修改或删除任何需求或者目标都将自动适用于特化用例,这是泛化关系中继承特性的具体体现。
在建模实操中,或许有建模者直接使用上图中各叶子结点用例建模,即将上图简化为仅有现金支付、信用卡支付、借记卡支付、支付宝支付、微信支付五个用例,这种建模方式虽然也能实现付款的目标,但它也使得模型中参与者与用例之间的关系变得复杂。以下两张图(图 2和图 3)分别是在使用泛化关系和不使用泛化关系并同时加入参与者的建模结果。可以看到,在使用泛化关系时,参与者“顾客”只需要与用例“付款”建立关联,而不使用泛化关系时则参与者“顾客”需要分别与各个具体付款用例建立关联,此时虽然用例数量少了,但参与者与用例之间的关系却变得更加复杂了。
图 2 泛化用例与参与者关联
图 3 具体用例与参者关联
不使用泛化关系建模还存在另外一些问题,比如实现各个具体的付款用例时,各用例其实有着大量相似的操作与流程,因而在进行用例场景描述(用例实现)时将会产生重复工作。
此外,如果付款用例需要进行修正,在使用泛化关系的模型中,只需要修改“付款”一个用例即可,而在未使用泛化关系的模型中则需要修改每一个具体的付款用例。
基于泛化关系建模,也利于用例的扩展。例如对于“扫码支付”,当前仅实现“支付宝支付”和“微信支付”,后继如果需要追加其他扫码支持,例如“云闪付支付”,只需要在“扫码支付”下特化一个“云闪付支付”用例即可,对模型整体影响不大。
上图中还使用了“抽象用例”(UML用斜体的用例名称表示),抽象用例是不能直接实例化或执行的用例,它代表一组用例的共同行为和目标,它通常提取和封装多个具体用例的公共步骤或逻辑,以便实现用例之间的复用和模块化。
建模时,参与者与用例可能同时存在泛化关系。例如在图书馆借阅场景下,很多图书馆将读者(借阅者)分成两类,即仅能借阅图书的普通借阅者和允许借阅视听资源的视听借阅者。视听借阅者也可以借阅图书,也就是说视听借阅者可以拥有普通借阅者所有的特性,因而可以在视听借阅者与普通借阅者之间建立泛化关系;而借阅视听资源的具体流程与目标和借阅图书是相似的,只是被借对象除图书外还增加了视听资源,它是借阅图书的一种特化,所以这两个用例之间也存在有泛化关系。其建模结果如图 4。
图 4 用例与参与者同时泛化建模
类似地,在上述图书馆借阅场景下,如果不使用泛化关系,则视听借阅者就必须与借阅图书用例建立关联,如图 5所示。
图 5 不使用泛化关联建模
事实上,在图书馆借阅场景下,与普通借阅者相关联的用例不止一个,如果将它们全部建模,那么在上述用例图中,所有与普通借阅者有关联的用例,视听借阅者也必须通过实线与它们建立关联。
在本文最后,请读者思考一个问题,在图 4中,如果将两个泛化关系去除任意一个而保留另一个,这个模型描述的场景会变成什么样的呢?