6. 1 基于类的系统结构建模
通过顺序图或通信图确定了对象的行为模型后 ,现在可以根据对象和对象行为模型来建立类模型。建立类模型是整个软件分析和开发中最为重要的一个环节。通常,类的建模有两个目的: 一 是 建立模拟真实世界中的业务关系模型,即域模型( Domai n Model ) ,域模型解决的是功能性需求问题; 二 是建立使类与类之间可能产生最大松耦合关系的模型,即 设 计 模型( Des ig n M od e l ) ,设 计 模 型是在域模型的基础上解决软件的质量问题,即非功能需求问题。当完成了设计模型这个环节之后,意味着软件编程即将开始。无论是用于表达域模型的类图 ,还是 用于表达设计模型的类图, U ML 的类图表达方式都是完全一样的。
例如,通过对顺序图 4- 20 和通信图 5-1 的分析 ,可以 根据对象的行为模型来建立域模型类图,通过这个类图来描述类以及类与类之间的关系,如图6- 1 所示。

6.2 类 图
类图( C l a s s D ia g ra m ) 是 类的 模 型,是利用图示和文字注释描述类以及类和类之间相互关系的方法。类图是 UML 中最重要的建模图示语言之一,它用于建立类、类的内部结构(类的属性和方法)以及类与类之间的各种关系模型。类图是编程最重要的模型依据。
类图是由类( C la s s 、类之间的关系( R e la t io n sh ip ) 和约束 ( C on s t r a in t ) 构 成的。它的表达方式为:

6.3 类图的表示方法
6.3.1 表示类
在 U M L 中,用 矩形框来表示类。一般将矩形框分为三部分,最上方为类的名字, 中 间 为类的属性,下 方 为类的方法
图 6- 2 是两个类的示例

在实际应用中,只有类名是类图中唯一不可缺少的部件,而类的属性和方法都可以根据具体需要来决定是否表示在矩形框内。
如果需要,还可以向类图中增加其他栏用于表示其他预定义或者用户定义的模型特, 例如用于表示事务规则、职责、变化、信号处理和异常处理等。附加分格的顶部写有分格名字,并用特殊的字体与其内容区分开,如图 6-3所示

1. 类的名字
在 UML 类图中, 类用一个矩形框表示。在这个矩形框中,类的名字是不能省略的,其 他 组成部分,如 属性和方法则可以根据类图的使用目的而省略。在系统分析设计阶段,可以用任何语言为类命名。但是,一般用英语,因为这可以直接与编程对应, 英文命名的规则是类名的首字母要大写。如果类名中包括多个单词,应该把每个单词 的首位字母均大写。还要注意的是,正体字书写的类名说明类是可被实例化的类,即 具 体 类( ConcreteClass ) ,斜体字说明类为抽象类( AbstractClass ) ,接口( Interface ) 则用构造型的方式来表示。例如,具 体 类 OrderMgt、抽象类 Storage和接口 Product 的命名如图6- 4所示。

2. 类的属性
在类的矩形框的属性区域内,U ML 用以下语法来描述类的每个属性:

上述语法都采用了斜体字,说明 表示属性的语法的每一个部分都是可以省略的。图 6- 5 给出了 O r d e r 类的类图,这里只列出了 O rd er 类的名称和属性,而忽略了O rd e r 类的方法。

为了方便将类图和代码对照,下 面给出图 6- 5 所对应的一段 J ava 代码。

下面结合 O rd er 类,对表达类的属性的语法做出说明。
l ) 可见性
可见性( V is ib li t y ) 指根据可见性规则( V i s i blit y R u le ) ,一个方法或属性是否能被另一个方法访问。例如,经 p u b l i c 修 饰 的 类、方法或属性可以被任何其他的方法或属性访问。可见性规则是为了使类在使用时更安全,尤其 是 对 属性的访问控制的定义更体现了封装的概念。表 6-1 按 照被系统其他部分可访问的范围 ,由高到低地对可见性的修饰词、描述及对应的 J ava 访问控制符给出了说明。

如图 6- 5中 ,属性orderID具有公共可见性,说 明 它 可被系统中所有的类访问和使用; sta t us 用“#“修饰,表示它可被类 Order、Order 所在的包中的其他类及可能在其他包中的Order类的子类所访问; 属性 price 具 有私有的可见性,说 明 它只能被Order类自身所访问。
2) /
"/”表示当前属性是导出属性(Derived A ttribute),是可经类的其他属性计算得出的。在属性前加“/“可以提醒实施者,当 前 这 个 属性可能并不是必需的。 UML规范指出,导出属性是只读的( ReadOnly ) ,用 户不能更改它的值。图 6- 6给出了导出屈性的例子。

其中,导 出 属 性 amo untPayment是 float类型的私有属性,它 记 录了订单中产品的总计应付款额,它来自于订单中预订的所有产品的单价与数量的乘积的总额,这个属性完全可以通过计算得出,是个 衍 生值,故被设计成导出属性 ,用“ /”表示。
3) 属性名
如图 6- 5中的属性 price、orderM gr 等都是属性名( Attrib ute Name) 。 属性的命名也有一些原则 ,应该用首字母小写的名词为属性命名,如 果 属性名中包括多个单词, 除 了 第 一 个单词外,应该把其余单词的第一个字母大写。
4) 属性的类型
用冒号分隔属性名和属性的类型。例如 orderID : S tr i ng 和 amo untP aym ent : float。
5) 多重性
多重性(Multiplicity ) 指明该属性类型有多少个实例被当前属性引用。表示方法为:

不指明多重性,则 表示多重性是 1。多重性也可能是一个简单的整数,或者是用”..“分 开的一个值的范围。用“* ” 表示多重性的上限,说 明 上限是无限的,如果 仅放置一个“* ” 表示多重性,则 说 明 多 重性为 0 或多。下面是多重性的表示方法和含义的例子。
(l) 1 :只有一个。
(2)0..1: 0 或 1 个。
(3) 0.. * 或* : 任意多个。
( 4 ) 1.. * : ] 或多个。
(5) 3. . 4或 6: 确切数目,如 3或 4个,或只有 6个。
(6) 0. . 1, 3. . 4, 6.. *: 更复杂的表示方法,表 示除 2~5外的任何多个数目。
6) 默认值
可能注意到有的时候需要在程序中为某个特殊属性设置默认值( Defa ult ) ,例 如屈性 s ta t us 的 默认值为 N u ll ,一个新银行账户的余额的默认值应该为零等。
7) 属性字符
属性字符( PropertyString ) 用 于说明属性具有的其他性质,经 常用特殊的文本指明 ,例如 ,具 有 readOnly属性说明当前属性是只读的,用 readOnly修饰的展性在Java中将被声明为 final。 如果 一个属性多重性大于 1,那 么它可能具有 ordered属性 ,表明 当 前 属性中的元素应该被顺序存储 ,或者具有唯一 ( Unique ) 属性,说 明 当前 属性中的元素都应该是唯一的,不允许 有重复值,图 6- 5中属性 productNames就属于这种 情 况。 所 以,在 Java程 序 中, productNames被 定 义 为 privateHashSet
< S tri ng > prod uct Na mes =new H as hSe t < Stri ng > () ; 以 保 证 prod uct Na mes 的 唯一性条件。
8) 约束
约束( Co nt ra i n t ) 表示对属性的约束和限制,通常是用“{ }"括起的布尔类型的表达式。更多时候,可能更愿意在注释 ( No te ) 中说明属性的约束,然后用虚线将其与它说明的属性连接起来 ,如图 6- 5 中的注释。
3 类的方法
类的方法( Method ) 说明了类能够做什么。在类的矩形框的方法区域内, U ML用以下语法来描述类的每个方法

在画类图的时候没有必要将全部的属性和方法都画出来。实际上,在大部分情况下也不可能在一个图中将类的属性 和方法都画出来,只将感兴 趣的属性和方法画出来就可以了。图 6- 7 给出了类 O r d e r M a n a g e m e n t 及其方法 。
下面以类O r d e r M ana g e m e n t 为例 ,说明类的方法的语法及其应用 。
1
) 可见性方法的可见性可分 别用+、# 、~、— 表示 public、protected、package、private4个不同的级别,其含义在表 6- 1中给出了 表述。
2) 方法名
类的方法名应该用首字母小写的动词,如果方法名中包括多 个单词,除了第一个单词外,应该把其余单词的首字母 大写。
3) 参数列表
指明方法的参数列表,如果该 方法没有参数,则参数列 表可以省略,但空括号还要保留。参数列表的格式为:

参数列表的方向( D i r e ct io n ) 可能是 in、inout 、o u t 或者 re t ur n 。如果该关键字不存在 ,方向为in。 in 表示参数将被调用该方法的调用者传入; in o u t 表示参数将被调用者传入,经当前方法修改并 传回给调用者 ; o u t 表示参数不会被调用者设定 ,但是将被当前方法修改并传回; r e t ur n 表示参数的值将被作 为返回值传回给调用者。
参数名的命 名方式与属性 的命名一致。
type参 数的 类型; multip licity 表 示 多 重 性; d efa ult 是参 数的默 认 值。properties指与参数相关的特性,将在特性部分给出解释。在方法名及其参数列表与 方法返回值类型之间用冒号分隔,各参数间用逗号分隔。
4 ) 方法的返回值
如果方法没有返 回值,那么 ret urn - type 为空。
5 ) 特性
特性( properties) 用于说明方法具有的其他性质,代表附加在元素上的任何可能值。常用的特性包括precondition、postcondition、query、exception、bodyCondition 等。precondition指明一个方法被调用前系统必须处于的状态; postcondition指明一个方法被调用后 系统处 于的状态; query说明方法将不对类的属性做修改,当前方法仅是一个查询方法; e xception指明方法可能引 起的异常; bodyCondition对方法的返回值作约束。在图 6- 7中,方法 calculateTotalCost( )包括一个 precondition性质: cart.items. count> O,它说明calculateTotalCost ()方法在执行之前必须计算产品总数撮,只有总数呈大于 0才可以计算总值。在方法 ship ltems ( destination : Address ) 中包括一个前置条件 paymenthasbeenverifi ed,它指明为产品添加运输信息时必须先验证付费已经完成。
参 照 上面 给 出 的 语 法 说 明, 对 OrderManagement类 做 出详 细 解 释。OrderManagement 类的名字是 OrderManagement,它是一个包含 5 个方法的类,这5个方法都具 有公共可见性 。方法 getO rder( orderID : String) 的名字是 getOrder,它拥有一个 String类型的参数 orderID; 方法 addItems 拥有一个 Product 类型的参数item,该方法的返回值为空,即 VO中 方法 minus ltems也只包含一个参数 item,是一个 Product类的数组,向该参数传递的值应为互不相同的 0到多个一系列 的数值,minusltems的返回值为空,该方法执行完成之后 应有对 totalitems的检验,以保证该方法执行完成之后totalitems的值大于等于0,否则将有相应的机制来处理total
ite ms 小于 0 的情况 ,如抛 出一个异常; 在执行 ca lc ul a te T otalCos t 方法的时候首先应对 ca rt. it e ms. co un t 进行判断,只有当它的值大于 0 的时候才执行cal cu la teT otalCos t 方法,这种判断可以使用 Ja va 语言中的 i f 语句,该方法没有参数,其返回值类型为flo at ,该 方法的情况 与方 法 ca lcu l a te T ota lCos t 类 似,也是首先进行判断,即当payment 被验证之后才执行ship I tems 方法,该 方法具有一个 Addr ess 类型的参数des t i natio n,如果该方法成功执行 ,则返回 t r ue ,否则返回false。
4 类的 静态属性和静态方法的 表述方法
静态的属性和方法被称为静态 类成员。在类图中,用下划线标明该属性或方法是静态成员 。在 O rd er 类中,为了设定所有订单开具发票情况的 标志,为 Ord er 类定义了几个静态属性和方法,如图6- 8 所示。

在Java语言中,用关键字static说明类成员是静态成员。它在类加载时完成初始化,并且保持到该类被清除为止,在此期间,类及其实例共享这同一份数据,同时静态方法也在类加载时被执行。例如,静态属性orderinvoiceSatus NONE,用]ava语言可以定义成下面的形式:p r ivatestaticStringorderlnvoiceSatusNONE= 'B';静态方法getOrderlnvoiceSatusNONE可以定义为:

6.3.2 类的关系
至此,对于表达一个类及类的方法和属性,应 该 没有问题了。但是还需要表达这些类的关系( R e la t io n s h ip ) ,因为类是通过各种关系彼此相互联系的。类的关系有下面 4 种:
1 .关联
关联( A s s o c ia t io n ) 表示一个对象拥有另一个对象。关联指两个类之间的 h as a的关系。关联描述了有着共同的结构和语义的一组对象之间的连接。
图 6- 9 是一个关联的简单例子,它 表示 O r d er 拥有 P ro d uc t ,而 P ro d uc t 也拥有O rd e r。
注意,具有关联关系的类互为成员变量,也就是 说 ,为了表示 O r de r 和 P ro d uc t之间的关联关系,要在O rd er 类中定义一个 P ro d uc t 类的成员变量,在 P ro d uct 类中定义一个 O rd e r 类的成员变量,用下 面的 J a va 代码表示。


为了能够表现现实世界对象间的关联关系,关联需要更多的属性,下面来详细讨 论关联的属性。图 6- 10展示了 Person和 Company两个类之间的带有属性的关联关系

关联具有下面的属性。
l ) 关联的方向/导航
关联的方向属性表示可以通过关联关系从关联类导向到目标类上。可以用实线来表示关联关系。如果表达从一个类到另一个类的方向,可用带箭头的实线表示关联的方向,阅 读 者将沿着这个箭头来阅读关联。
如图6-10所示,公司拥有雇员,则公 司和雇员关联关系用一条带箭头的线相连,且箭 头指向目标端。反过来 ,如果关联的一方与另一方没有关系,则在实线的尾部画一个叉。如果这种关联的方向是双向的,那就不需要任何箭头,直 接用直线将相互关联的类相连就可以了
图 6-10 的关联关系表示公司拥有雇员,这是一种单向关联,在用代码实现如图 6-10 所示的关 联关系的时候,
P erso n 被设计为 Co mpany 类的一个成员变虽。
public class Person{
}
publ i c class Company { Per son person;}
2) 关联名
为了方便人们的阅读,关联通常有一个名称,这个名称应该选用一个动词词组。关联关系通常是在分析过程中命名的,此时还没有足够的信息对角色进行适当的命名。如果使用关联关系名称 ,关联关系名称就应该反映该关系的 目的,关联关系名 称应放置在关 联关系路径上或其附近 ,并且用一个实心 箭头表示关联名称的发生方向, 在图 6- 10中,Person类和 Company类的关联名为 works for,名称是有方向的,它的方向用实心的箭头表示。关联名称 是不出现在编码中的 ,它不能被映射 为代码 。
3) 关联角色
关联关系的两端为角色 ,角色规定 了类在关联关系中所起的作用 。
每个角色都必 须有名称,而且对应 一个类中所有角色的名称都必须是唯一的。角色名称应该是一个名词 ,以描述在特定 的环境中关联的行 为或职责。关联角色是对 一个关联的特殊说明,关联角色的命名应能够表达被关联关系对象的角色与关联关系对 象之间的关系,也就是说,关联的命名应根据类在关联关系中为与它相关联的类做了 什么,而不是根据这个类本身 是什么。
如图 6- 10 中,Com p any 的合适角色名称可以是 em plo yer , Perso n的角色应该是emplo yee ,避免使用“有“和“包含” 之类的名称,因为它们不能提供有关类间关系的信息。
值得注意的是,关联关系名称和角色名称的使用是互斥的:不能同时使用关联关 系名称和角色名 称。角色名称通常比关联 关系名称更可取。在分析阶段 ,因为没有 足够的信息来正确命名角色,可以 选用关联名称 ,但在设计阶段中应始终使用 角色名称。如果没有好的角色名称,可能意味着模型不完善或构建不合理。
角色名称被放置在紧邻关联关系线的末端。
4) 多重性
多重性表示一个类同时拥有的实例的数目,它描述的是一个类的多少对象与另一个类的一个对象相关,可用 一个单一的数字或一个数字序列表示。多重性应放在被拥有的类的附近。
图 6-10 表示公司可以拥有多名雇员 ,而一名雇员只能在一个公司工作。下面的Java代码可以演示这种多重性。

5 ) 关联的类型
前面讨论的关联指两个类之间的 has a 的关系,其 实现实世界中这种 has a 关系却是可以进一步再分的。
例如一辆汽车,它 有车轮和车篷,但是车轮和车篷对于汽车来说并不是同等重要的。一辆汽车可以没有车篷,但是却不能没有车轮。可见,关 联所表示的 h as a 关系是有强弱的,根据这种强弱,可以将关联关系进一步细分为: 一 般 关 联、聚合和组 合。一个人为一个公司工作,这种关系可以将其表为一般的关联关系,如 图 6- 10
所示。
聚合( Aggregation ) 是一种强类型的关联,它表示 isthepartof或者 ownsa的关系, 是一 个装配件类与某个部件类相关联的一种关系。带有多种部件的装配件应包含多个聚合。
关联与聚合有什么区别呢?实际上,关联与聚合之间的区别是比较模糊的,关联 是否应该建模成聚合并不是显而易见的,何 时 使 用 聚合需要判断,没 有统一的规定。通常,建模需要经验的判断,没有一成不变的规则。
我们的经验是,只 要经过了仔细的判断,并在建模过程中保持一致的意见,那么 聚合和普通关联之间的区分实际上并不会产生问题。
正是因为关 联与聚合之间这种模糊的区别,UML 决定包含聚合和一种被称为组合的更强类型的聚合。这样,UML 就包含两种类型的部分-整体关系: 两个对象按照部分-整体关系绑定的普通形式称为聚合,有更多限制的形式称为组合。
组合( Co m po s it io n) 是 某种更强形式的聚合。组合意味着整体与组成部件之间是互不可分的关系,作 为整体的类会因为拥有某个作为部分的类而存在,否则整体也会消失。
以 PPS 项目中的 产品 摩托车为例 。一辆摩托车包含的零件包括机油泵( O il Pump汃发 动机( Engine ) 和无线电( Radio ) 等,其中,对于摩托车来说 ,机油泵和发 动机是必不可少的,它们与摩托车的关系被建模 为组合,而无线电 则被建模为聚合。
用实心的菱形表示组合 ,用空心的菱形表示聚合 。相应的类图 如图 6-11 所示。

也许你现在正想 为上面所示的 摩托车与其部件的类图编写程序,有一点请注意 : 被设计成聚合关系的装配件类中应该包含部 件类的成员变量,在编程的时候,最好在创建这个成员变禄的时候就为其赋初值 ,以防止其值 为空。被设计成组合关系的装配件类中也应该包含部件类的成员 变量,在编程的时候,应强制创建这个 成员变量的时候为其赋初值,以防止其值更改 。虽然这样的做法可能引 起今后更改不易,但也应提醒程序员 注意这点 。
在关联关系中 ,还有几个方 面值得注意。
(l ) 自关联。
自关联( S e lf- as socia t io n ) 指一个类与其自身存在一种关联关系。能不能将其理解为该类的某个实例与其自身 存在关 联关系呢? 其实,更多的情况下 ,自关 联关系意味着该类的某个实例与该类的其他实例 之间存在关联关系。
举个例子说明 这个问题,如图 6-12 所示。

在此示例中 ,一名雇员 与其他多 名雇员存在关联关 系,从图 6-12 中可以看出,这名雇员的角色是经理,负责管理多名角色为工人 的雇员。由于雇员知道他们的经理, 而经理也 知道他的属下 ,所以该关联关系能够双向导航 。
图 6-1 2 的示例与如图 6-1 3 所示示例所表达的意思 是一样的。

自关联关 系的 情况 下,角色名 称对于区分 关联关系的目的非常重要。图 6-1 4 是一个部门的类图,这是一个自关 联加多 重性的例子。

它表示一个部门有一到多个雇 员,其中一名是该部门 的经理。经理负责 领导其他多名雇员。一个雇员只能为一个部门工作。
( 2 ) 关联类。
考虑下 面的悄 况,如图 6-1 5 所示。

假设公 司做了下面的规定: 在一段时期内,你只能为一家公 司工作,我们需 要为受雇于每个公司的雇员保存他的受雇时间。那么,雇佣合同中关于雇员受雇时间的属 性应该属于 Co mpa ny 类还是 P erson 类?
解决方案是创建一个雇佣类 Employment ,将受雇时间的信息保存在该 类中,这样的类称为关联类( Assoc iat ion C lass) ,如图 6-1 6 所示。
要将拥有属性或行为的关联关系组织为关联类,可以向关联类中添加属性、方法 和其他关联的特点。通常关联类最常见的用途是协调多到一或多到多关系。
除了受雇时间外 ,还有一些信息 ,如薪水 、工作类型、参加工作 时间等,将这些 信息存放在关联类中 ,要比将这些信 息安插在 Com pa ny 或 Pe rso n 类中处理都要好。

但上面的类图看起来会给人一种假象: 看起来好像是P ers o n 类与 E m ploym e nt类关联 ,E mp lo ym e nt 类又与 Co m pa ny 类关联。
如何识别关联类呢? U M L 用一条从关联关系路径到类符号的虚线表示 ,其中类符号包含此关联关系的属性、方法和关联关 系,如图 6-17 所示。

P er so n 类与 Co m pa ny 类都通过关联类 E m ploym ent 彼此相互关联。属性 per iod被保存在 E m plo ym e nt 类中。
原则上,关联关系和关联类的名称应该是一致的,但 是 在必要的情况下,也可以使用不同的名称。一个退化的关联类只包含此关联关系的属性,在这种情况下 ,可以忽略关联类名称,以弱化其独立性。
在根据上面的类图创建 J a va 类的时候,P e r so n 类 中 将包含一个 E m plo yme n t 类的成员变量,Co m pa ny 类中也将包含一个 Em plo ym e nt 类的成员变量,但 Pe rs on 类中将不再包含 Co m pa ny 类的成员变量,同样,Co m p a ny 类也不再包含 P e rs o n 类的成员变最。
( 3 ) 限定关联。
考虑一种情况: 假设两个类之间存在关联关系,但其 中 一个类与另一个类的一部分实例存在关联关系,而与这个类的另一部分实例不存在关联关 系,这就涉及两个类发生关联关系的资格问题,称为限定关联( Qualifi edAssocia tio n ) 。 在类图中,用 限定符( Qualifier ) 表 示限定关联,它 用 来选择关联联系起来的对象。
限定符的表示法是在关联线靠近源类一端绘制一个小方框,这个小方框可以放置在源类的任何一侧。这样,源类 加上限定符就产生出目标类。
考虑下面的例子:公 司 规 定 ,只 有具有职员编码的员工才拥有定期奖金的奖励,并 不是 所有的人都与奖金有关联关系(例如一名兼职员工),只有具有职员编码的人才与奖 金 有 关 系, 如 何 反 映 这 种 情 况 呢? 可 以 为 这 个 关 联 关 系添 加 限 定 符em ployee N um ber ,表示奖金与员工的关系是通过职员编码实现的,如图 6- 18 所示。

再来看一个例子:银行为多个账户服务,一个账户只属于某个银行。银行和账户的关联关系的多重性为一对多,如 图 6- 1 9 所示

但是,如 果 在 Ba nk 类中添加 acco un t N um ber 属性,应用“银 行 十账 号”的 组合,那么 至多会产生一个账户。也就是说,限定 符 对 目 标 对象进行了选择,将 有 效 的 多 重性从“多“降为“一” 。
这 个 限 定 关 联例子的类图如图 6- 20 所示。

在设计的时候,可以将 A cco u nt 类的其他属性忽略,但 acco u nt N um ber 属性却不可忽略。
(4 ) 关联上的异或约束: xo r 。
具有一个公共类的二元关联之间可能存在异或约束,把这种结构称为 xo r 关联, 如 图 6- 21 所示。

U M L 把 xor 关联表示成连接两个或者多个关联的虚线,虚线上标有约束串{ xor} 。xor关联表明: 对千公共类的任何单一实例 ,一次仅可实 例化多个潜在关联中的一个关联,即某时 刻只有一个关联实例。图 6- 21的例子表明,关联类 Account中或者有一个 Company类的成员变量 ,或者有一个 Person类的成员变最,但不可能 同时拥有 Company类和 Person类的成员变量 ,或者没有 任何 Company类或Person类的成员变量。也就是说 ,Company和 Person不能同时被 Account拥有。 xor关联也可以通过泛化的方式来表达,如图 6- 22所示就是 xor关联的另一种表达方式: 泛化。

2. 泛化
在实际生活中 ,有许多事物都具 有共同的特征。其实,泛化( Gen era lization ) 是人类推理中最 基本的概念: 从众多的个性化个体中发现它们的共同点。泛化是指父类与其一个或多个子类之间的关系。父类拥有公共属性、方法和关联,子类除了具有父类的属性、方法和关 联之外,还具有自己 的特征。每个子类继承(I nher it ) 其父类的 特征。类之间存在相似性和差异性 ,应用泛化,子类共享定义在一个或多 个父类( P aren t Class ) 里的结构或行为。
泛化有时被称作 is a kind of 关系,来看一个例子,如图6- 23 所示。

例子中展示的就是一个泛化层次关系。ProductStorage 和 PartsStorage都是Storage类中的一种类型,在这组类中,因为它们都有一组相同的属性 和方法,所以 把这组属性 和方法定义在 一个超类中,并把它们之间的关系定义为泛化。父类是 一般的元素 ,子类 则是更特殊的元素。 在 Java中,用 extends关键字来直接表示这种关系,如:

在 UML中,泛化关系被表示为一个带有空心三角形箭头的线段。人们通常喜欢把泛化关系组织成一棵树 ,箭头所 指的方向是父类,箭头起 始端是子类。从父类到子类 的方 向 被 称 作 特 化 ( Specialization) , 例 如 从 Storage到 ProductStorage和PartsStorage ; 反之 ,被称为泛 化( Generalization) ,例如从ProductStorage 和 PartsStorage 到 Storage。特化指的是子类约束或特例化父类 ,泛化指的是父类泛化子类 。
3. 实现
在对软件系统进行逻辑设计的时候,有时软件的设计者只关心某个类或某个部件 所提供的服务的规 格说明 ,例如它们应提供哪些方法、方法的 调用规则和格式是怎样的,而不关心 这些服务是如 何实现的。对类的服务进行这样的说明以后,可以 使设计者把思路集中在类或部件所提供的服务上,而不必拘泥千它的实现,该类或部件的实 现留待设 计的稍后时刻再考虑。
在 UML里,有一个专门的建模元素可以用于对类或部 件所提供的服务进行描述,这就是接口 (Interface ) 。UML接口描述的是 一系列的方法 ,这些方法为 一个类或部件规定了其必须提供的服务。
接口被建模 为实现( Realization) 关系。实现关系将一种模型元素(例如类)与另一种模型元素(例如接 口)连接起 来,由 实 现关 系 指定二者 之间 的一个合 同( Co n t ract ) ,一个模型元素定义一个合同,而另一个模型元素保证履行该合同。也就是说,关系中的 一个模型元素只具有行为的定义,而行 为的具体实现规则是由另一个模型元素来 给出的 。
先来看下面的例子,如图 6- 24 所示。

UML应用虚线加上空心的箭头来表示实现关系。关系中的箭头由实现接口的类指向被实 现的接口。接口只是行 为的定 义而不是结构或实现,接口中的属性都 是常量,方法都是抽象方法,没有方法体 ,因而 接口只有与外界接触时输入、输出格式的定义。接口只是一个口,它的里面是空的。在 Java中,实现关 系可直接用 implements 关键字来表示。
UML2. 0 将接口简化为图6-25 表示的接口形式,称其为 lollipo p ,它是一个供接口,是类提供的对外接口,它表示类能够提供的服务,然后可以在类图的某个地方定义lollipop表示的 接口。

4.依赖
1 ) 依赖和依赖的类型
依赖( De p e n d e n c y ) 是两个事物间的语义关系,其中一个事 物(称为服务的提供者)发生变化,会 影 响 到另一个事物(称为客户或服务的使用者),或向它(客户)提供所需信息。在类与类之间应用依赖关系指明一个类使用另一个类的方法或一个类使用 其他类所定义的属性和方法。
在 UML 中 定 义 了 4 类 基 本 依 赖 类 型,分 别 是 :使 用 ( U sag e ) 依 赖、抽象( A bs tra ctio n) 依赖、授权( P ermiss ion) 依赖和绑定(B ind ing ) 依 赖 。
使用依赖是一种非直接的依赖,它 通 常表示使用者使用服务提供者所提供的服务,实 现它的行 为。 可 选 择的使用依赖关键词有: us e 、ca ll 、s e nd 、parame te r 和 i ns ta n t ia t e 等。
抽象依赖建模表示使用者和提供者之间的关系,它依赖千在不同抽象层次上的事 物。可选择的抽象依赖关键词有: t r ace 、refi ne 和 der iv e 等。
授权依赖表达了一个事物访问另一个事物的能力。提供者可以规定使用者的权 限,这是提供者控制和限制对其内容访问的方法。可选择的授权依赖关键词有: access、import和 friend等。
绑定依赖,用于 绑 定 模 板 以 创 建 新的模型元素,可选择的绑定依赖关键词主要为
bind 。
表6-2给出了UM L 基本模型中的一些依赖关系、关键词和它们的含义。

2 ) 依赖的表示方法
在图形上,把一个依赖关系画成一条有方向的虚线,箭尾处的模型元素(客户) 依赖于箭头处的模型元素。箭头上可带有表示依赖关系类型的关键字,还可以有名 字 ,如图 6- 26所示。

3 ) 依赖与关联
依赖是一种使用关系,它表示了一个事物说明的变化可能影响到使用它的另一个事物,但反之未必。也就是说,服务的使用者以某种方式依赖于服务的提供者。而关联是一种结构关系,它详述了一个事物的对象与另一个事物的对象相互联系。
依赖与关联最关键的区别在于,存在依赖关系的两个类 A 和 B,类 B 不 是 类 A 的成员变量,如图 6- 27 所示

而存在关联关系的两个类 A 和 B,则 类 A 中肯定有一个类 B 类型的成员变量,如图 6- 28 所示

6. 4 总 结
前面已经提到建立类模型是整个软件分析和开发中最为重要的一个环节。本章 层层深入、系统地阐述了 U M L 类图建模方法的三个基本建模元素(类、关系和约束) 的图示和语法。
在实际软件开发项目中,不需 要在建立每个 U M L 类图时都详细地描述类的所有属性、方法和关系。记住类图建模是问题的抽象,对 类图描述的详细程度取决于所关注类的层次。例如,在建立域模型(Doma i n Mode l) 时 ,关 注的 是实现需求的基本功能点是什么,以及组成这些基本功能点的基本单位是什么,所以,这时关心的是类的选择,而不是类的属性。如图6-1 就是一个典 型 的 域模 型类图。但是,在建立软件的设计模型(Des ig n Mode l) 时 ,关 注的是软件的质量(Q ua lit y ) ,需 要设计每个类及类与类之间关系的细节,这时,图 6-1 就显得粗糙了,需要 建 立更为详细的类图来层层深入地描述每个类的内部结构(由类的属性和方法组 成)以及类之间的关系。

