包是 UML 的主要组织结构。打包的元素往往是内聚的,并且仅与包外的元素弱耦合。开发团队获得了一组包,可以在没有太多重叠的情况下工作。
在图 8.10 中,我们展示了一个简单的叫车应用程序包结构。这种方法适用于拥有叫车团队和支付团队并希望分工的组织。如果我们的划分正确,他们就不会经常需要协调。尽管单独的支付包团队需要与定价包团队进行一些协调,但他们不必以其他方式进行协调。所示的这种方法基于功能分离。
另一种更推荐的方法是生成以用例为中心的包,请参阅第 14 章,行为:序列图。在这种方法中,您将为每个 Actor 构建一个包:Rider、Driver、Management,并在每个包中放置关联的用例和类。您仍然需要包来覆盖基础设施。
分层方法通常很常见,重点是将项目分离到包含硬件和操作系统接口的低级包中;
中间层包包含 实用程序、数据库包;
更高级别层的包包括 应用程序逻辑。
顶层是用户界面包,见图 8.11。
在大多数项目中,您可以结合几种不同的组织原则。例如,用例方法可用于将 UI(用户界面)和应用程序逻辑层划分为包。然后可以通过分层方法将架构和基础设施分布到单独的包中。
应该调整包的大小,以便持续的开发和维护不需要太多的协调。
8.3.1 包的依赖
包可以依赖于其他包。 例如,支付包中的包取决于定价包。 如果定价包发生变化,可能其他包也需要更改,见图 8.12。 当依赖项的数量最小化时,这是一个很好的包架构。 我们通过用虚线箭头从依赖项指向独立项来显示依赖项。 图中3个Package依赖于Pricing Package,但是在图8.11中,依赖箭头应该怎么画呢?
8.3.2 来自其他包的特定元素
在我们在第 8.2 节中讨论的情况下,上面的包和可见性我们只需要在元素名称之前放置 PackageName::,因为包(P1 和 P2)彼此直接可见。 想象两个 Package 链,A1、A2、A3、...、A10 和 B1、B2、B3、...、B10,其中每个 Package 包含下一个较低级别的Package。 如果 A10 中的一个元素要引用 B10 中的一个 C 元素,则限定名会很长; 它将是B1::B2::B3::B4::85::B6::B7::88::B9::810::C。 这很笨拙,因此 UML 具有简化技术:导入和访问。 这些技术是对上面讨论的依赖项的改进,详细说明了依赖项的含义。
8.3.2.1包导入
将包 A10 与一个特殊的依赖项(虚线箭头)连接起来,指向标有 «import» 的 B10。 然后,将整个 B10 包视为已复制到 A10 中。 如果可见性规则和隐藏规则允许,B10 中的元素对A10 中的元素直接可见,见图8.13。 如果发生名称冲突,则会复制有问题的元素,并且必须使用限定名称以老式且冗长的方式进行引用。
8.3.2.2 元素导入
元素导入
如果 A10 只需要 B10 的几个 Elements,而不是导入整个 Package,您可以将依赖箭头直接连接到您要导入的 Elements。 这使得对所需元素的简单引用变得简单,而不会用你不想要的元素挤满命名空间。 通过不暴露不必要的元素来减少出错的机会,参见图 8.14。
如果 Element 导入会导致名称冲突(即如果导入包中已经存在具有该名称的 Element),则忽略该导入。 如果 A10 中已经有 C,但您仍然需要 B10::C,则可以向导入添加别名。 导入标签看起来像“«import» D”。 然后在 A10 中,B10::C 的用户将只引用别名 D。
8.3.2.3包与元素导入的比较
让我们看看这些导入的结果。 左边的虚线元素是一种传统方式,表示元素作为导入的结果存在,但实际上驻留在它们的原始位置,见图。 8.15 和 8.16。 元素导入仅带来选定的元素,而不会带来可能不需要的元素,而包导入则带来整个包。
8.3.2.4 访问
访问
使用包和元素导入将目标作为公开可见的项目引入包中,以供导入包中的其他元素使用。作为公开可见的项目,导入导入包的任何人都可以进一步看到它们。如果您使用 «access»,而不是 «import»,您会得到相同的结果,但该项目被认为是私有的,不能进一步导入。
在图 8.17 中,Types 包的公共成员被导入到 ShoppingCart 包中。 当 WebShop
Package 导入 ShoppingCart Package 时,Types 的公共成员会进一步导入 WebShop Package 中并可供使用。
但是,Auxiliary 辅助包的成员(私有和公共)仅被私下导入到 ShoppingCart 包中,因为使用了“访问”。 当 WebShop 包导入 ShoppingCart 包时,它不会看到或使用辅助包中的任何内容。
在下图中,我们显示了导入和访问之间的区别。导入包的可见性设置为公共 (+),访问包的可见性设置为私有 (-)。 C 元素的可见性没有改变(图 8.18 和 8.19)。
8.3.2.5 包依赖和循环
许多建模者制作了大量图表,显示了包之间的依赖关系和其他关系,以指导他们将元素放置在正确的包中。 这是一个很好的做法。 它可以揭示包之间危险的循环依赖关系。 例如,如果PA依赖于PB而PB又依赖于PA,那么协调将非常困难。 如何决定将某物放在哪里以及如何确定包的开发顺序? 如果模型强制你在Packages 之间有循环依赖,那么将链中涉及的整个 Packages 集合封装到一个更高级别的Package 中,隐藏依赖关系并将整个链分配给一个团队。
8.3.2.6 Package Merge 包合并
包合并根据复杂的规则从现有包创建一个新包。 在 UML 2.x 的早期版本中使用了合并,但发现它过于难以使用。 UML 2.5 不使用它们,但它们仍在 UML 规范中定义,以防其他 OMG 规范可能依赖于它们。 合并可能会从 UML 的未来版本中删除。
需要记住的要点
• 包之间的依赖关系由指向依赖包(源)的独立包(目标)的虚线表示。
避免包之间的循环依赖
• 除了标准依赖之外,建模者可能会指出需要在源包中复制包
公开复制的包 «import»“导入”关系
私下复制的包由«access»“访问”关系指示。
• 也可以使用<<import> 关系导入单个元素。