包是UML中管理元素的有效手段,UML中的所有元素均隶属于某一个包,即使你没有指定元素所属的包,这些元素也会被置于一个默认包中,包的本质是命名空间。当我们在一个包中需要访问另一个包中的元素时,可以使用符号“::”逐级引用。
例如在下图所示的包图中,存在两支包含多个层次的包,如果包A3中的元素需要访问包B3中的元素C,则可以使用B1::B2::B3::C的形式达到目的。
不同包中的元素进行交互是常态而不是偶发现象,使用符号“::”引用其他包中的元素,特别是在包的层级较深时,元素前面会有长长的由“::”连接的包路径。对人类而言,这种冗长的形式无论是书写还是阅读都是一项具有挑战性的工作,它影响的不只是效率,可读性也大打折扣。为解决这个问题,UML提供了包导入的机制,例如在上面这种情况下,我们可以将包B3导入到包A3中,其图形化表示如下图所示。导入关系使用由导入包发出终止于被导入包的虚线箭头表示,并需要标注“<
一旦包B3被导入包A3中,包B3就如同成了包A3的一个虚拟的子包,在遵循可见性规则的前提下,包A3中的元素就像使用子包中的元素一样使用包B3中的元素。在下图中,使用虚线在包A3中描画包B3,表达了这种虚拟的关系。
注:关于UML中可见性的说明,可参见文章《修饰符》中“可见性修饰符”部分。
包的设计本质上应有其内在逻辑含义,但客观上它也是解决命名冲突的一种手段,而通过import向一个包导入另一个包时,如果被导入的包中与当前包中存在同名元素就会产生名称冲突。在这种情况下,被导入包中的冲突元素将被忽略而不被导入,如果需要使用这个产生冲突的元素,我们将不得不继续使用“::”的形式来进行意图表达。这就正如当前包中的元素与子包中的元素名称产生冲突时一样。
如上文所述,将一个包全部导入到另一个包中可能会产生诸如名称冲突或其他潜在问题。如果一个包只使用另一个包中一个或少数几个元素,更好的做法是仅导入那些我们需要的元素。例如包A3仅需要使用包B3中的元素C,则在导入时,可仅导入元素C,在图形描画上,导入箭头直接指向元素C即可,如下图所示。
仅导入需要的元素时,将只有被导入的元素可直接使用,而那些与被导入元素位于同一包的其他元素对当前包则需要继续使用符号“::”引用。在上述示例中,被导入的元素C在使用逻辑上也如同直接在包A3中定义一样,而元素D则是包A3之外位于其他包中的一个普通元素。
仅导入特定元素降低了导入产生潜在问题的可能性,但是也有可能被导入的元素与当前包中的元素碰巧产生了冲突。例如我们要在包A3中导入包B3中的元素C,但包A3中已经包含了一个名为C的元素,导入产生名称冲突。此时,我们可以使用“<
上述对import的说明重点在import使用和细节上,但其实<
在下图中,同时描画了包之间的import和access关系,包B访问包A1而导入包A2。
在UML的场景下,包B中的元素使用包A1和A2并无二致,我们可将包A1与A2均视为包B中虚拟的子包,但这两种依赖关系的效果有一个明显的区别,即“<
在编程时包之间的依赖关系如果设置有误,往往会在程序编译与运行时发生异常,UML仅通过图形表达相关关系,不同UML工具对依赖的检查并不一致,所以需要仔细检查确认。
参考文献:
1.《OCUP 2 Certification Guide_ Preparing for the OMG Certified UML 2.5 Professional 2 Foundation Exam》 Michael Jesse Chonoles
2.《OMG® Unified Modeling Language® (OMG UML®) Version 2.5.1》