
1.3 类间关系
类间关系大致分为关联、依赖、继承、实现、组合和聚合,下面将通过“学籍管理软件”的实例来逐一说明。
1.3.1 关联
关联表示两个或多个类间存在的一种语义关系。这种关系可以是单向的,也可以是双向的,它使得类之间可以知道彼此的属性和方法。当人们在系统建模时,特定的类会彼此关联。这里将首先详细讨论常用的类间关联关系。
1. 双向关联
关联是两个类间的联接。如果关联是双向的,那就意味着两个类彼此知道它们间的联系,除非我们限定一些其他类型的关联。如获奖情况(AwordsSituation)的例子,获奖情况和优秀毕业生之间存在一种双向关联的关系。根据获奖情况自身的方法,可以判断一名学生是否为优秀毕业生。同样的,优秀毕业生本身拥有获奖情况这一属性(当然知道自己所获奖项)。图1-1显示出在获奖情况(AwardsSituation)类和优秀毕业生(OutstandingGraduates)类之间的一个标准类型的关联。

图1-1 类间双向关联例图
一个双向关联用两个类间的实线表示。在线的任一端,可放置一个角色名和多重值。图1-1显示AwardsSituation与一个特定的OutstandingGraduates相关联,而且AwardsSituation类知道这个关联。因为角色名以AwardsSituation类表示,所以AwardsSituation承担关联中的“awardsSituation”角色。紧接于AwardsSituation类后面的多重值描述0..1表示:当一个AwardsSituation实体存在时,可以有一个或没有OutstandingGraduates与之关联(也就是,OutstandingGraduates可能还没有被分配)。图1-1也显示AwardsSituation知道它与Outstanding-Graduates类的关联。在这个关联中,OutstangdingGraduates承担“isOutstandingGraduates”角色;图1-1告诉大家,OutstandingGraduates实体可以不与AwardsSituation关联(例如,他还未毕业)。
由于对那些在关联尾部可能出现的多重值描述感到疑惑,表1-1列举一些多重值及其含义的例子。
表1-1 多重值和它们的表示

2. 单向关联
在一个单向关联中,两个类是相关的,但是只有一个类知道这种联系的存在。图1-2显示单向关联的奖学金的一个实例。Scholarship是根据分数确定奖学金等级的,而分数却不知道奖学金。因此,这是一种单向关联。

图1-2 单向关联实例
一个单向的关联,表示为一条带有指向已知类的开放箭头(不关闭的箭头或三角形,用于标志继承)的实线,单向关联包括一个角色名和一个多重值描述,但是与标准的双向关联不同的是,单向关联只包含已知类的角色名和多重值描述。在图1-2的例子中,Scholarship知道Score类,而且知道Score类扮演“awardsLevel”的角色。然而,和标准关联不同,Score类并不知道它与Scholarship相关联。
3. 关联类
在关联建模中,存在一些情况,即某个类需要包括其他类,因为它包含关联相关的有价值的信息。对于这种情况,开发者会使用关联类来绑定其基本关联。关联类和一般类一样表示。不同的是,主类和关联类之间用一条相交的点线连接。图1-3显示一个就业推荐实例的关联类。根据获奖情况判断是否为优秀毕业生,如果是,则作为优秀毕业生推荐工作。
在图1-3显示的类图中,在AwardsSituation类和OutstandingGraduates类之间的关联,产生了称为ExcellentRecomend的关联类。这意味当AwardsSituation类的一个实例关联到OutstandingGraduates类的一个实例时,将会产生ExcellentRecomend类的一个实例。

图1-3 增加关联类ExcellentRecomend
4. 反射关联
现在已经讨论了所有的关联类型。就如读者可能注意到的,我们的所有例子已经显示了两个不同类之间的关系。然而,类也可以使用反射关联与它本身相关联。起先,这可能没有意义,但是请记住,类是抽象的。图1-4显示一个RollStudent类如何通过checkIfColonel角色与它本身相关。当一个类关联到它本身时,这并不意味着类的实例与它本身相关,而是类的一个实例与类的另一个实例相关。校级三好学生也是三好学生的一种,三好学生本身具有isColonelRollStudent方法来判断自身是否为三好学生:通过自身与校级三好学生的对比。因此,这是一种反射关联,即便两名三好学生不是同一对象,但从属于同一类。我们这里讨论的是类的范畴。

图1-4 反射关联实例
图1-4关系说明一个RollStudent实例可能是另外一个RollStudent实例的上一级。然而,因为“checkIfColonel”的关系角色有0..1的描述;一个三好学生可能不是校级三好学生。
1.3.2 聚合
聚合是一种特殊的关联关系,是“强”关联类型,是整体到部分的关系。在基本的聚合关系中,部分类的生命周期依赖于整体类的生命周期,而部分类的生命周期独立于整体类的生命周期。
举例来说,我们可以想象,获奖情况是一个整体实体,而证书是整个获奖情况的一部分。证书可以独立存在于获奖列表。在这个实例中,CertificateAwards类实例清楚地独立于AwardsSituation类实例而存在。然而有些情况下,部分类的生命周期并不独立于整体类的生命周期,这称为组合,是更“强”的聚合。举例来说,考虑学院与专业的关系。学院和专业都建模成类,在学院存在之前,专业不能独立存在。这里Professional类的实例依赖于College类的实例而存在。
让我们更进一步地探讨基本聚合和组合聚合。
1. 基本聚合
有聚合关系的关联指出,某个类是另外某个类的一部分。在一个聚合关系中,子类实例可以比父类存在更长的时间。为了表现一个聚合关系,需画一条从父类到部分类的实线,并在父类的关联末端画一个未填充棱形。图1-5是奖励和证书的聚合关系的例子。

图1-5 基本聚合关系的例子
2. 组合聚合
组合聚合关系是聚合关系的另一种形式,但是子类实例的生命周期依赖于父类实例的生命周期。图1-6中显示College类和Professional类之间的组合关系,注意组合关系如关系一样绘制,不过这次菱形是被填充的。

图1-6 组合关系的例子
在图1-6的关系建模中,一个College类实例至少总有一个Professional类实例。因为关系是组合关系,当College实例被移除/销毁时,Professional实例也将自动地被移除/销毁。组合聚合的另一个重要功能是部分类只能与父类的实例相关(如我们例子中的College类)。
1.3.3 继承
在面向对象的设计中,一个非常重要的概念——继承,指的是一个类(子类)继承另外的一个类(父类)的同一功能,并增加它自己的新功能(一个非技术性的比喻,譬如作者本人继承了其父亲的音乐能力,但是在其家中他是唯一一个玩电吉他的人)。为了在一个类图上建模继承,从子类(要继承行为的类)拉出一条闭合的,单向箭头(或三角形)的实线指向超类。考虑三好学生的类型:图1-7显示ColonelRollStudent和ProvincialRollStudent类如何从RollStudent类继承而来。校级和省级三好学生继承了父类的属性studentName(public),并扩展自己的属性extraAwards。

图1-7 继承箭头说明
在图1-7中,继承关系由每个父类的单独的线画出,这是在IBM Rational Rose和IBM Rational XDE中使用的方法。然而,有一种称为树标记的备选方法可以画出继承关系。当存在两个或更多子类时,如图1-7中所示,除了继承线像树枝一样混在一起外,可以使用树形记号。图1-8是重绘图1-7的继承,但是这次使用了树形记号。

图1-8 树形记号继承实例
1.3.4 实现
一个类和一个接口不同:一个类可以有它形态的真实实例,然而一个接口必须至少有一个类来实现它。在UML 2中,一个接口被认为是类建模元素的特殊化。因此,接口就像类那样绘制,但是长方形的顶部区域也有文本“interface”,如图1-9所示。

图1-9 接口类实例
在图1-9中,Scholarship类实现了Money的接口,但并不从它继承。这主要有下面两个原因。
(1)Money对象作为接口被定义它在对象的名字区域中有“interface”文本,而且我们看到由于Scholarship对象根据画类对象的规则(在它们的名字区域中没有额外的分类器文本)标示,所以它们是类对象。
(2)我们知道继承在这里没有被显示,因为带箭头的线是点线而不是实线。如图1-9所示,一条带有闭合的单向箭头的点线意味着实现;正如图1-7中所示,一条带有闭合单向箭头的实线表示继承。
1.3.5 依赖
依赖关系也是类与类之间的联接,依赖总是单向的。依赖关系在Java或C++语言中体现为局部变量、方法的参数或者对静态方法的调用。

图示用点线即虚线加实心箭头表示,如图1-10所示。
1.3.6 包
如果某人正在为一个大的系统或大的业务领域建模,其模型中将不可避免地会有许多不同的分类器。管理所有的类将是一件令人生畏的任务。所以,UML提供一个称为软件包的组织元素,如图1-11所示。软件包使建模者能够组织模型分类器到名字空间中,这有些像文件系统中的文件夹。软件包看上去只是对类进行分类,对于一个巨大的工程来说,没有软件包很难理解,有时候还会出现不必要的错误。软件包能够使结构清晰化、层次鲜明。

图1-10 类依赖关系

图1-11 包图
下面简介可见性。
在面向对象的设计中,存在属性及操作可见性的记号。UML识别4种类型的可见性,它们是public、protected、private及package。
UML规范并不要求属性及操作可见性必须显示在类图上,但是它要求为每个属性及操作定义可见性。为了在类图上的显示可见性,放置可见性标志于属性或操作的名字之前。虽然UML指定4种可见性类型,但是实际的编程语言可能增加额外的可见性,或不支持UML定义的可见性。表1-2显示了UML支持的可见性类型的不同标志。
表1-2 UML支持的可见性类型的标志
