缩略语
    UIS     UML Infrastructure Specification   UML基础结构规范
    UML    Unifed Modeling Language            统一建模语言
    USS    UML Superstructure Specification  UML上层结构规范

参考资料

    《UML Infrastructure Specification, v2.2》
    《UML Superstructure Specification, v2.2》
    《UML类图中的关联、聚合和组合
    《UML类图中的依赖
    《UML类图中的泛化
    《UML类图中的类
    《UML包图中的包和命名空间

1 包引入

    包引入(package import,参见USS的7.3.39节)是一种允许采用非限定性名称访问来自于另一个命名空间中的元素的关系。假如我们有一个包A和一个包B,如果包A没有引入包B,那么包A在访问包B时,必须采用限定性名,比如B::Integer。当包A引入了包B以后,则可以采用非限定性名称进行访问,此时A可以直接用Integer来访问包B中的Integer。对于包的引入,其如同C++语言中的using namespace关键字,也如同于Java语言中的import关键字。
   
    图 1是包引入在UML规范中的语法定义,从图中你可以看出它包含一个importingNamespace和一个importedPackage,其意思是将importedPackage引入到importingNamespace。此外,我们可以看出PackageImport“是一个”直接关系。还需注意的是PackageImport包含一个visibility属性用于表示包引入关系的可见性。在UML规范中定义了这一属性值可以是public或是private。其含义是:如果一个包引入可见性是public,那么,被引入的包可以被命名空间之外的其它元素访问;如果一个包引入可见性是private,那么被引入的包只能被命名空间中的元素访问,而命名空间外的元素不能访问。这与我们编成语言中的public和private的含意是一样的。

图 1包引入在UML规范中的语法
    图 2是包引入的一个例子,其中也示例了包引入关系在UML中的表示方法。可以看出包引入关系与依赖关系的表示方法是相同的。在包引入的表示方法中,带有箭头一侧的包被引入到没有箭头一侧的包中,这可以从图 1中的语法定义看出,因为importedPackage是target。你可能要问,图 1中只定义了包引入是将一个包引入到命名空间中,但图 2中却是将包引入到包中,这样对吗?还记得《UML包图中的包和命名空间》中所提及的包在UML中的语法定义吗?因为包是从命令空间继承来的,也就是说包“是一个”命名空间!

图 2 包引入的一个例子
    前面所说的包引入的可见性分为public和private,在UML中分别采用不通的图型(sterotype)来区分。public对应<<import>>图型,而private对应<<access>>图型,这一点在图 2中也有示例。从图 2中我们可以看出,ShoppingCart包引入了Auxiliary包和Types包,当引入了以后在ShoppingCart包中可以直接采用非限定性名对两个引入包中的元素进行访问。此外,Types包还可以被ShoppingCart包之外的元素访问,因为其引入关系的可见性是public,也就是说WebShop包引入了ShoppingCart包后,也可以直接采用非限定性名访问Types包中的元素。由于Auxiliary包引入到ShoppingCart包的可见性是private,所以,虽然WebShop引入了ShoppingCart包,但并不能采用非限定性名访问Auxiliary包。当然,WebShop包仍然采用Auxiliary::xyz这样的限定性名访问Auxiliary包中的元素。我们还可以理解为,<<import>>关系是可传递的,但<<access>>关系则不可以。

2 包合并

    包合并(package merge,参见USS的7.3.40节)定义了一个包的内容是如何被另一个包扩展的关系。包合并关系表示将两个包的内容合并在一起从而得到一个新的合并包,当然,这种合并关系也隐含了对被合并包的扩展。图 3列出了UML规范中包合并关系的语法。

图 3 UML规范中可打包元素的语法
    图 4是使用包合并的一个例子,包合并关系在UML图中的表示与依赖关系是一样的。从图 3中的语法可以看出箭头(target)所指向的包是被合并的包。图 4中我们能看到三个包,即Merged包、Merging包和Importing包。我们还可以看到Merging包将Merged包合并了,以及Importing包引入了Merging包。三个包中都有一个A类,这里的A类只是为了说明方便,其实,包中可以有其它的元素,那三个包中的A类在合并前后有什么关系呢?

图 4 包合并的一个例子
    为了方便理解这一问题,请看图 5。其中的加号表示的是合并这个操作,等号的左边表示合并之前,而等号的右边表示合并之后。在合并之前,我们可以看出Merged::A和Merging::A分别是一个扇形,但合并之后Merging::A就变成了一个圆,因为Merged包被合并进了Merging包,这个合并操作是站在Merging包的角度来看的。从Merged包的角度来看,不论是合并前或是后,都是一个扇形,这还是比较好理解的,因为它是独立的,并没有合并其它的包。对于Merging包我们还可以这样理解,在合并之前,无论是从包里头看还是从包外头看都是一个扇形。但在合并之后,从里面看来A还是一个扇形,但从外面来看却是一个圆。

图 5
    有了上面对于包合并的理解后,我们就不难理解Importing包中的A了,显然它是一个圆,是从Merging包的外部来看引入的A。

3 总结

    包引入这一关系是为了让一个命名空间能更方便的引用另一个包中的元素,其方便性在于对于引入的包中的元素可以采用非限定性名的访问方式进行访问。包合并关系概念的引入是为了建模包的增量式扩展,即我们可以在已有的一个包上通过包合并从而得到一个功能更加完整或是强大的新包。为了读懂UML规范,我们需要对包引入和包合并有很好的了解。

致读者

    如果你想参与讨论UML相关的话题,请加入UML技术圈(g.51cto.com/UltraUML)。