9. 1 基于包的系统静止状态下的结构建摸
在分析、设计软件系统时,会发现随着对用户需求的分解,软件系统的功能越分越小,越分越多,相应的模型也越建越多 ,最终很难识 别诸多模型的建模元素( Element ) 的归属。因此急需按照某种要求来划分这些建模元 素的所屈范围 ,以便更容易理解所建的诸多 U ML 模型和它们的建模元素的作用。UML 包图( Packag e Diag ram) 是一种有效的建模工具,它为基于包 ( Packag e) 的系统在静止状态下的结构建模。在 UML 包图中,每个 包如同操作系统中的文件夹, 可以根据需要建立相应的文件夹结构,然后,把相应的模型和模 型元件放入其中。这样在查找某个模型或模型元件时,能够知道它们的位置。
因为用例图和类图在设计和开发过程中更倾向于扩张,所以,包图比较常见的是用于用例图中的用例和类图中类的分群,以便保持用例图和类图在系统功能上的清晰 划分。实际上包图可以应用于任何 U M L 建模图中,这完全取决于建模是子系统或某种区分的需要。
9.2 包 图
包提供了一种 UM L 元素分类和命名空间( N a m es pace ) 定义的方法。几乎所有的 U M L 元素都可以用包来分组,而且包还可以 嵌套。包的本质意义在于下面三点:
(1 ) 在逻辑上把一个复杂的模型模块化。
( 2 ) 按一定的规律为相关元素分组。
( 3 ) 定义命名空间。
命 名 空 间
一般地,命 名空间用于识别一系列 来源不 同但是名字相同的元素。命名空间是一个抽象的容器,这个容器把相同名字的不同元素分开包装,使它们具有不同的上下文环境,这样在同时使用名字相同但是意义不同的元素时,每个元素可以利用自己的命 名空间相 互区分。
U M L 包图展示了包及它们之间的关系,它的抽象表达方式为:

U M L 包图的语义简单,但意 义重大,它用 U M L 符 号 表示模型元素的组合。系统中的每个元素都只能为一个包所有,一个包可嵌套在另一个包中。使用包图可以将相关元素归入一个系统。一个包中可包含附属包、图表或单个元素。
9.3 包图的表示方法
9.3.1 包
U M L 使用一个左上部带有标签的矩形表示包。图 9-1 表示了包 secur i t y。

包中元素的表示方式 有两种 。一是在包中用矩形画出这些元素 ,这种方法下 ,包的名称就可以放 在包图左上部的标 签 中。例如,要表示 包 s ec ur it y 中的元素C red ent i als 和 Id ent it y V er i fier ,可以用 如图 9- 2 所示的方式 。

第二种表示方法是用实线将包和包中的元素连接起来。这种 方法的包含关系用接近包那 一端带有加号的圆圈来表示。使用这种表达方 式的时候,最好是把包名 称放在大矩形框中 ,这样 ,就可以 在表示包元素的矩形框中添加更多关于包元素的细节。应用这种方法,如图 9- 2 所示的包图可以表示为如图 9- 3 所示的形式。
系统自身定义了最外层的命名空间,它是所有名字的基础。它是 一个包,通常还带有几层嵌套的包,直到得 到最终基 本元素的名字为止。也就是说,包还可以 包含其他的包,例如,包ja va 可以包含包ut i l,包ut il 包含类Date 。这种结构可以用如图9-4 所示的形式 表示。


由于用如图 9- 4 所示的形式表示多层的嵌套将很麻烦,因 此 UML 用双分号(:: ::) 隔 开的命名空间的形式表示这种嵌套关系。应用这种方法,图 9- 4 分别与如图 9-5 所示的两种形式等价。

9.3.1 包中元素的可见性
包中元素的可见性主要有下面三种。
(1) + : 表示公共的可见性( P u b lic ) ,这类元素可以被包外部的所有元素访问。
(2) # : 表示受保护的可见性( P ro t ect ed ) ,这类元素仅可被继承该包的子包中的元素所访问。
( 3 ) 一: 表示私有可见性( P r i va te) ,这类元素不能被包外部的元素访问。
9.3.2 包之间的关系
包之间有三种关系:访 问 ( Access ) 导入·( I mpo rt ) 和合并( Merge ) 。UML 用带开箭头的虚线表示包之间的关系。
1. 访问
包的访问关系详细地说明了被导人的元素具有私有的可见性。U ML 用构造型< < acc ess> > 加在虚线上表示包之间的访问关系。来看如图9-6所示的例子。

该例中, us ers 被 称 为源包 ( So ur cePac kage ) , s ec ur it y被称为目标包( Target Pac kage ) 。
这个例子表示包 u se r s 要用到包 sec ur it y 中的元素。由于可见性的原因,us ers 中的元素 U se r 只能使用 sec ur it y 中的元素 Credent ials 和 Ident it y V er if i e r ,而不能使用MD5Cryp t。
2.导入
包的导入关系指目标包中的内容将被导入到源包中。当然,目标 包 中 的 私 有 成 员是不能被导入的。导入关系用构造型< < i mport > > 加在虚线上表示。
来看如图 9- 7 所示的例子。
这个例子表示包 u sers 中 的 元 素 Us er 可以访问包 sec ur it y 中的元素 Credent ials和 Id ent it yV eri fi e r ,可以用如图 9-8 所示的形式形象地进行说明。
那么,包的 access 关系和 import 关系有什么不同呢? 注意,假设 包 us e rs 被导入到另一个包 Z 中,如果 users 和 s ec u r it y 是 access 的关 系,则 Z 中的元素是看不到包secur ity中元素 C redent ia l s 和 Id ent ityV er ifi er 的 ,即 包 s ec ur ity 中的元素 C rede nt ia ls和 Id e nt it y Ver ifie r 对 包Z 中 的 元 素来说 ,其可见性是 pri va te。
与access 关系不同,如果包use rs和包secur it y 是im port 的关 系,那么 ,包 Z 中的 元素是可以见到包 secu r it y 中元素Credentia ls和Ident it yVerifier的,也就是说,包 sec urity 中的元素C redent ials 和Identit yVer山er对包Z 中的元素来说,其可见性是public。


3 . 合井
合并关系表示将目标包中的内容合并到源包中去。同样,目标 包 中 的 私 有内容也是不能被合并的。合并关系用<< merge > > 加在虚线上表示,箭头指向被合并的包。如图9-9所示为包 sales合并了包inventory中的元素。

包的合并关系包含下列规则:
(1 ) 包中的私有成员不能被任何其他包合并。
( 2 ) 如果合并的包中的类与被合并包中的类重名或具有相同的类型,那么在合并后的包中将存在一个泛化关系来建模合并的包与被合并包中的这些类。
( 3 ) 可以仍然使用被合并包的名字加类名的方式引用被合并包中的类。
( 4 ) 合并包和被合并包中的类都会保持原样不变被添加到合并包中。
( 5 ) 即使被合并包中还存在子包,子包也将被添加到合并包中。
( 6 ) 如果被合并的包中有一个子包与合并包中的子包同名,那 么 这两个子包之间还会合并一次。
( 7 ) 任何与被合并包具有导入关系的包都将作为合并包的导入包 ,与合并后的包建模为导入关系,就 是 说 ,不会产生规则( 2 ) 所说的泛化关系,被导入的元素不被合并。如果导入的元素与合并后包中的元素有冲突,那么 ,导入包中的同名元素具有优先权, 被导入的元素必须被重新命名。
为了说明这些规则,可看如图 9-10所示的例子。

图 9-11 所表达的意思与图 9-10是一致的。
值得注意的是,m er g e 关系是在设计阶段实现的,迄 今 为止还未受到程序设计语言的支持。m er ge 关系有助于更改设计时使开发者了解设计者的意图

9. 4 总 结
本章介绍了 U ML 包图。应该牢记:应 用 包 的 目 的 是 为了简化图。通常当一个图变得很庞大且在单一页中无法打印的时候引入包 ,此时 可以通过采用分而治之的方法把大的图重新组织为较小的图。
包图中可以包含任何一种 UML 图,但通常更多的是用例图或类图。
创建用例包图,可以帮助组织需求,对需 求进行高层次的 概述。创建类包图,可以在逻辑上组织类,对设计进行高层次的 概述。
在创建类包图的时候,一般 会 采用垂直分层的方式组织包中的类,以 显示类以及类之间的关系。一般地 ,应该把相同继承层次的 类、彼此间有聚合或组 合关系的类、彼此合作频繁且信息能够通过 U ML 顺序图和 U ML 协作图反映出来的类组织在相同的包中。
用例通常是面向对象开发方法学中主要的需求制品之一。对千大的项目来说 ,通常创建用例包图( Use Case Package Diag ra m) 来组织需求。
组织用例应该以主要角色的需要为基础。用例包图可以包含角色,这样做将使用例包图更容易被读者所理解。可以水平地排列用例包图,由于用例包图的主要使用者 是项目的开发者,因此图的组织应该能够反映他们的需求。应该把关联的用例放在一起,即 将具有 i ncl ude 、extend 和 generalizat i on 关系的用例放在相同的包中。
下面是一些包建模 的技巧:
(1 ) 包的命名要简单 ,可以用描述性的名称为包命 名。
( 2) 将高内聚的模 型元素放 在一个包中,例如,属于继承关系的父类和子类应放入相同的包内;类与类之间组成关联关系时,这些类应放入相同的包内;如果类与类 之间有紧密的协作关系时,这些类应当放入相同的包内。
( 3) 注意命名含义,即被导入到一个包中的元素并不知道导入它的包对它做了什么。
( 4) 避免包间的循环依赖: 包 A 依赖于包 B,包 B 依赖于包 C,而包C 依赖千包A,这就形成了一个循环: A- B—C—A 。循环依赖是 一个很好的 信号,意味着需 要重构一个或多个包 ,把导致循 环依赖的因素从包中除掉。
( 5) 包依赖应该反映内部关系: 当一个包依 赖于另一个包时,意味着这两个包的内容间存在着一个或多个的关系。例如,如果 是一个用例包图,那么就有可能两个 用例之间存在 include、extend或inher it ance关系,而两个用 例分别处千不同的 包中。

