接下来呢就是要讨论一下这个包含关系和扩展关系的区别。
因为正是因为这个包含关系和扩展关系呢,这个 不同的人有不同的理解,造成很大的一些混淆和歧义,
所以不同的专家也对此呢有不同的观点。
这样呢 这个有几种观点我们这个大概总结一下,第一种观点呢,
认为包含关系和扩展关系是既相同又不同的两个概念。
所谓相同呢是它们是都是表示一种将小的 usecase
插入到大的 usecase 中的一种机制,并且呢这个不管是
扩展这个 usecase,还是包含这个 usecase ,是吧,都是不完整的,都是
非常小的,都是不可能脱离这个 那个主要那个 usecase 而存在的。
在这个 都是与这个过程化的那种这个编程语言中
这个主程序和子程序有很类似的一个地方。
但是最重要的是它们的不同点,不同点呢第一个就是很容易观察到的这个,或者很容易区分就- 是方向不同。
因为包含关系呀,是吧,它实际上是指的是 大的包含小的,主要的包含这个
次要的,所以呢它是由这个主要用况 发起,然后目的地呢是指向那个小用况和不完整用况。
而这个扩展关系呢,它这个语义是从这个,由一个小的扩展一个大的,所以呢它是 必须呢是这个由这个不完整的小的那个
usecase 指向那个完整的比较大的那个 usecase。
所谓大小呢实际上是表示它们这个描述 usecase 这个详细描述中的那个序列到底是多还是少。
另外呢就是说当我们这个
为了描述一种复用关系,比如说这个登录啊,一些这个查询呀,这些 在很多的一些不同的一些这个大的
usecase 中会调用 一些可重复利用的小的场景的时候,
我们一般呢要选择包含关系,并且这个调用的时候呢, 它是无条件的调用,就是直接就是不管你是什么条件,
然后执行到我这个位置,那马上要调用那个子程序。
这是 当这个一对多的时候,当出现一个
子用况,有多个主用况来调用它的时候,一般来说要选择 包含关系。
这个当这个反过来,多对一的时候,多个子用况,一个主用况
的时候,这个主用况在任何一次执行的时候,不一定非得把多个子用况全部执行过来,
而是呢根据条件它可能选择其中某
几个或某一个来执行的时候,这时候呢 是选择扩展关系。
因此呢,另外一种这个 判断方式就是说包含关系呢,它既然它没有包含点,它也是处理这个
一般的情况,无条件的,这个不管 什么一种情况都雷打不动的要调用过来子用况的时候,这时候用包含关系。
但是,如果说有条件的,是吧,我需要这个进行条件判断,当条件成立的时候才调用的时候,
这叫处理特殊情况,这时候呢要用扩展关系,这是一种观点。
另外一种观点呢,是这个比较早期的这个 UML 这个傻瓜书上的一个观点,这个傻瓜书呢就是说这个
基本上是对于初学者或者说是完全没有这个基础的一些人来看的,所以呢它
特点呢是这个非常浅显,然后呢这个在,当然它也讨论到这个比较深入的一个问题。
但是它给出了一个非常简单的答案,就是列了一张表格,然后让这个初学者这个根据表格
来打一个对号和错号,然后就可以非常容易的判断。
这个表格呢就是说 跟其实跟上一个观点非常像,就是如果这个
usecase 是可选的,那就是说是有条件的才执行的, 那就要用扩展关系,不要用这个
这个包含关系,如果呢这个没有这个 usecase,这个基本的
usecase 是否完整, 如果说是的话,那么呢它就是这个
这个扩展关系,否的话是吧,那就选这个包含关系。
实际上就是说这跟那个可选呢,还是有很大的一个相似。
然后这个 usecase 执行的时候是
有条件执行还是无条件执行,如果无条件选包含关系,如果有条件,就是有条件判断,那就是- 选扩展关系。
这个 usecase 是否改变了基本 usecase
的行为, 如果说是改变了,没有改变那么选否,那么就是
包含关系,另外这个如果说是这个改变了,那么选 这个扩展关系。
就是通过这几个,但是呢 这个如果说是存在矛盾的时候到底是这个
怎么选择?这里边就并没有说的非常清楚。
下面呢这个是这个 usecase 之父 Ivar Jacobson
实际上在这个一篇文章,他发表的一篇文章,《usecase 的昨天,今天,明天》这本
这篇文章中啊,他给出了自己当时 为什么要设计这个包含和扩展关系的一个初衷。
就是他实际上呢 我们发现很,实际上后续的这些
包含扩展,包括 UML 中的一些对这个包含关系和扩展关系的读解实际上都背离了
Ivar Jacobson 在最初发明这个 usecase 包含关系和扩展关系的一个 最基本的一个立足点。
就是说他也是强调二者是 既有相同的,又有这个不同的,两个问题。
这个相同的这个我们就不说了,跟前面我们说的是非常像的,这个作为不同点呢, 就是说什么呢,这个 Ivar Jacobson
实际上他这个观点跟其他的这个 包括这个当今 UML 它是不同的。
就是说在这个 包含关系上,基本的这个,这个 usecase
它代表着一个 控制流,它呢这个是这个主动的
调用那些这个包含的这个 usecase
,也就是说主动权在 主动对象在这个基本的这个
usecase 的时候,被,那个被动对象呢是那些什么呢?
那些这个包含的那些小的 usecase
封装的时候, 这时候呢要用这个包含关系。
也就是说主动权在我的 基本的这个 usecase 的这个手里边的时候,在执行的时候,这时候我们用的是这个包含关系。
当然反过来,这个 主动权,调用的时候主动权,这个不是这个最基本的这个
usecase, 知道什么时候调用,什么时候不调用,而是呢,主要的这个
usecase 在这执行,然后扩展的那 usecase 呢 也是封装了一个线程或者进程,它也是一个主动对象,
它的一方面主要的这个基本的这个 usecase,这个序列在执行,
另一方面那些扩展 usecase 在旁边坚守,在旁边这个
作为一种坚守线程在那等着,当它在监督它的执行的一些状态,
当它执行到某一个状态的时候,这个,这些这个扩展 的这些 usecase
所封装的那些这个线程啊,或者说那些主动对象呢, 就会向它发送消息,请求当前主动对象
暂停,然后呢执行自己的这块,执行完之后呢再恢复那个线程, 主线程,这种情况下呢用扩展关系。
所以我们可以看到,实际上今天的所有的 这些我们真正用这些扩展关系和包含关系,
完全背离了这个 usecase 之父,这个 Ivar Jacobson
最初发明这个它们 的这个初衷,因为他实际上呢,我们都是给他转义了,因为
Ivar Jacobson 真正使用这种这个 usecase
的那个 领域恰恰就是电信领域,确切的说就是交换机领域,所以呢,
这个他设计这个问题,主要是为交换机领域, 对于什么时候是主叫方是在这个
主动执行的时候还是在被,这个被动执行的时候,这种情况下来使用。
但是目前为止,很少有人 按照 Ivar 的这个最初的观念来这么使用这个扩展关系和包含关系。
这个再就是,也就是这个著名的这个面向对象这个 方法学者,这个
Martin Fowler 在《UML Distilled》这本书 翻译过来是这个《UML
精粹》这本书 中提出的,两个或多个独立的 usecase
重复自己并希望避免重复时, 那就要使用包含关系。
也就是说他这个观点呢和前面其实是非常 一致,也就是说它是表示为了复用的目的,那就用包含。
正常的行为的一种变化场景,并且这种场景呢临时表述,这就是说这个
表述正常的这种行为,这种,这个变化呢并希望临时这种表述的时候,这时候要用
这个我们后面讲到的这个泛化关系。
因为 这个泛化关系和这个包含关系和扩展关系不同的是
它可以对这个主要的这个,或者说它的父 usecase
进行修改,就是说这个包含关系和扩展关系都不能动这个父类
的一些东西,但是就是这个泛化关系可以修改一些某些行为。
就是说这个父类没有的行为,我可以给你修改上,或者说父类 以前是这么干,我可以这样干,这样的话呢它就会,它就这个
表示这个使用变化,但是要 有一个新的一种描述,这时候呢要用泛化关系。
我们后面 通过例子再这个明确这个问题。
这个什么时候用 扩展呢?就是当我们这个表述正常的行为的一个变化场景,这个跟前面那个这个泛化关系一样,
但是呢是希望使用更为受控的形式, 在基本的
usecase 中说明扩展点的时候,这时候要用扩展关系。
就是说它,尽管它这个表述的不太一样,但是它 这个初衷跟前面那个观点是基本上一致的,也就是一句话就是
复用的时候要用扩展,要用这个包含关系,
而表示这个变化,或者表示一种特殊的情况的时候, 这时候要用扩展关系。
另外呢就是我们教材上的一个观点,就是这个 也是这个比较独特的一个观点。
考虑到,主要是考虑到这个真正从实用的角度来 看,如果我们在这个工程实践中,软件工程开发实践中,
真正区分这个关系的时候,实际上呢, 表面上看来,前面说的几种都是很有道理,但是在
实践的系统中它非常复杂,很可能 这个同时具备了两个条件,这时候给这个开发人员呢,
无法这个真正的知道 还是这个不知道应该怎么使用。
并且呢二者语义其实差别 不大,都是表示如何在一个大的
usecase 中插入一个小的 usecase,实践中非常容易相混,
尽管有很多的专家说了很多一些这个 判定的一些这个条件,但实际上在
实际工作中呢,也是,即使有这些条件,这些条件往往会导致这个
推出非常矛盾的一个结果,后面我们可以通过例子也可以看出这一点。
另外呢这个就是说这个包含点和扩展点,包含点的这个提出, 和很容易给人造成一个
混乱的这个理解,就会问为什么只有包含点,只有扩展点没有包含点呢?是吧,
所以呢这个不太好,另外在对初学者来说这个方向经常搞混,所以呢
就不如这个建议在使用的过程中呢要淡化
弱化二者的区别,就是说这个比如说 只用一种关系来描述这种情况就可以了。
这个其实对于 这个开发人员来讲也是可以理解的,这是这个二者的区别。