UML聚合与关联

53

来自Martin Fowler的《UML精简版》:

在UML出现之前,人们通常对聚合和关联模糊不清。无论是否模糊,他们总是与其他人不一致。因此,许多建模者认为聚合很重要,尽管出于不同的原因。因此,UML包含了聚合(图5.3),但几乎没有语义。正如Jim Rumbaugh所说,“把它看作建模的安慰剂” [Rumbaugh,UML参考]。

我从中了解到的,并且在Stack Overflow上读到的答案是,使用这两种关系中的任何一种都没有关系,它们基本上是相同的东西。这是事实吗?还是有一些情况下,使用聚合而不是关联(反之亦然)可以被证明是合理的?

8个回答

32

Rumbaugh 的说法最有代表性,Uncle Bob的建议也很好。像我在其他地方所说的那样,聚合的语义含义非常弱,几乎没有任何实际利益。它只有一个有效的特例(递归关系的无环性),然而很少有人知道和理解这一点。因此,你最终还是不得不在注释中指出。

我只是不使用它。而且从来没有感觉到什么损失。坚持使用简单的二元关联,并专注于真正重要的事情-正确设置基数和命名。你会比试图决定无法判定的关联和聚合更有收获。

希望对你有所帮助。


你对组合链接有什么想法? - Dennis
@Dennis:与聚合不同,组合具有明确定义的语义,可以有用地将其与直接二进制关联区分开来。更多信息请参见:https://dev59.com/LFzUa4cB1Zd3GeqP22mu#7836357 - sfinnie
@sfinnie,您能进一步阐述递归关系的无环性部分吗?听起来很令人生畏。 - Mehdi Charife

31

或许这可以帮到您,但我不认为您会找到完美的解释:

区别在于暗示的关系不同。聚合表示整体/部分关系,而关联则没有。然而,两种关系的实现方式不太可能有很大的区别。也就是说,很难根据代码确定特定关系应该是聚合还是关联。因此,完全可以安全地忽略聚合关系。
[Robert C. Martin | UML]

以下是每种情况的例子:

a) 关联是所有对象都有自己的生命周期且没有所有者的关系。让我们以教师和学生为例。多个学生可以与单个教师相关联,单个学生也可以与多个教师相关联,但对象之间没有所有权,它们都有自己的生命周期。两者都可以独立地创建和删除。

b) 聚合是关联的一种特殊形式,其中所有对象都有自己的生命周期,但是存在所有权,子对象不能属于另一个父对象。让我们以部门和教师为例。单个教师无法属于多个部门,但如果我们删除部门,则教师对象将不会被销毁。我们可以想象“拥有”关系。
[Maesh | GeeksWithBlogs]


3
有趣的是,关于第二个例子,我认为如果我们将聚合和关联交换,仍然不会有任何区别,这归结于个人偏好和图表可读性? - Andna
1
你如何判断关系是完整还是部分? - reinierpost
@ChapMic "子对象不能属于另一个父对象",这是完全错误的 :) 请查看 https://dev59.com/WmzXa4cB1Zd3GeqPRTgn 和 https://en.wikipedia.org/wiki/Object_composition#Aggregation - igobivo
GeeksWithBlogs 的引用已经(实际上)失效。 它将重定向到 *Geekswithblogs.net,一个时代的终结*。 - Peter Mortensen

3
我倾向于使用聚合来展示与组合相同的关系,但有一个重大区别:包含类不负责所包含对象的生命周期。通常,将指向要包含对象的(非空)指针或引用传递给包含类的构造函数。在其生命周期内,包含对象依赖于已存在的所包含对象。未拥有所包含对象时,包含对象无法完全完成其工作。这是我对聚合所暗示的“部分/整体”关系的理解。

1
包含对象如果没有所包含的对象,就无法完全发挥其作用。这对于关联和某些形式的依赖关系也是如此吗? - Jupiter

1

这个术语经常会被混淆。

聚合和组合是一些关联类型。 在实现过程中,聚合和关联几乎没有区别,并且许多人在他们的图表中跳过聚合关系。

您可以从这个类比中理解。

Class:A(person)Class:B(car)之间有关联关系,如果Class:A有一个Class:B声明,并且Class:B(car)对象不是必要的来创建一个Class:A(person)对象。

Class:A(car)Class:B(tyre)之间有聚合关系,如果Class:A有一个Class:B声明,并且Class:B(tyre)对象是必要的来创建一个Class:A(car)对象。

干杯!


0
在UML中,聚合关系定义不够明确,因为它们没有任何明确定义的语义。Eric Evans在《领域驱动设计》中指出,聚合的一个有效用例是封装多个类。
例如,一辆汽车有四个轮子。您可能想要计算每个车轮行驶的总米数,对于每辆汽车都是如此。这个计算是由汽车实体完成的,因为它知道它有哪些轮子,而您不需要知道哪些轮子属于哪辆汽车。
汽车是所有部件(如轮子)的聚合根,您无法从聚合外部访问汽车的部件,只能访问根。
因此,基本上,聚合封装了一组彼此相关的类。

UML的组合在语义上更接近于DDD的聚合。DDD的聚合具有一些非常明确定义的语义(实际上甚至比UML的组合更强)。肯定比UML的聚合要强得多。很遗憾术语不匹配 :-( - sfinnie

0

另外,我建议从OMG网站下载UML规范:这是最好的参考资料,可以查看第110页。

None表示该属性没有聚合语义。

Shared表示该属性具有共享聚合语义。共享聚合的精确语义因应用领域和建模者而异。

Composite表示该属性被组合地聚合,即复合对象对于所组成的对象的存在和存储负责(请参见11.2.3中部件的定义)。


0

实现上没有太大的差异,但从概念上来说有很大的区别:聚合是用来表示层级关系的。当您在处理组件层次结构时,需要在根接口中具有某些类型的操作:

  • 在层次结构中查找子组件
  • 向层次结构中添加/删除子组件
  • 更改所有组件的共同属性
  • 递归遍历层次结构(访问者模式)
  • 重新配置层次结构和组件之间的链接(关联)

在处理关联时,大多数这些操作都不需要。


0
它们的意思不同!我可以这样表达: 关联关系:一个类引用另一个类。实际上它展示了一个类和另一个类相关,但它们不一定有属性来显示这种关系...例如 'Teacher' 和 'Student' 类,虽然 'Teacher' 类没有指向学生的属性,但我们知道在现实中老师确实有学生...还有 'School' 类有 'teachers' 和 'students' 属性,使这两个类相互关联。 聚合关系:一个类包含另一个类。但如果容器(教室)被销毁,被包含的物品(椅子)并没有被销毁。实际上,教室拥有椅子。聚合是比关联更强的关系。
这里还有一个关于整个 UML2.0 的教程,它易于理解并解释了所有内容,你可能会发现它很有用: https://github.com/imalitavakoli/learn-uml2

提示:还有一点需要提到的是,由于类之间通常存在关联关系,因此有时我们不会将其绘制出来,以避免不必要的复杂性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接