聚合与构成[关闭]


89

我很难理解UML中的组合和聚合之间的区别。有人可以给我一个很好的比较和对比吗?我也很想学习识别代码之间的区别和/或看一个简短的软件/代码示例。

编辑:我问的部分原因是因为我们在工作中正在进行反向文档活动。我们已经编写了代码,但是我们需要返回并为代码创建类图。我们只想正确捕获关联。



请在以下位置查看基于代码的示例: stackoverflow.com/questions/731802/…–
Almir Campos

UML 2.5澄清了差异。请参阅第40页的方框。110.所以我投票重新打开它。
qwerty_so

不,UML 2.5并没有阐明组成的定义,而是一直对此保持模棱两可,因为他们还说“在删除复合对象之前,可以从复合对象中删除零件对象,因此不能将其作为对象的一部分删除。复合对象。” 请参阅下面的我的答案(stackoverflow.com/questions/734891/…),在这里我试图阐明组成的含义。请提高我的答案以表明在SO中,正确的答案最终会获胜:-) [或降低所有错误的答案]
Gerd Wagner

Answers:


90

聚集与构成之间的区别取决于上下文。

以另一个答案中提到的汽车示例为例-是的,的确,汽车排气装置可以“独立”站立,因此可能与汽车的组成不同-但这取决于应用程序。如果您构建的应用程序实际上必须处理独立的汽车尾气(汽车商店管理应用程序?),那么聚合将是您的选择。但是,如果这是一个简单的赛车游戏,并且汽车尾气仅充当汽车的一部分-那么,构图会很好。

棋盘?同样的问题。没有棋盘,只有在某些应用程序中,棋子才不存在。在其他玩具制造商中,象棋棋子肯定不能组成棋盘。

尝试将合成/聚合映射到您喜欢的编程语言时,情况变得更糟。在某些语言中,区别可能更容易注意到(在简单的情况下,“按引用”与“按值”对比),但在另一些语言中可能根本不存在。

还有最后的建议吗?不要在这个问题上浪费太多时间。这是不值得的。这种区别在实践中几乎没有用(即使您具有完全清晰的“组成”,由于技术原因(例如缓存),您可能仍希望将其实现为聚合)。


1
我说的是国际象棋,而不是棋子,但都是有效点。
David M

2
我绝对对“这不值得”的心态表示怀疑。如果您不考虑是谁“拥有”一个对象并对其寿命负责,那么您将获得与对象CRUD相关的非常糟糕的代码,尤其是清理,由于对象层次结构处于不良状态,Null指针四处飞扬。
克里斯·凯塞尔

9
是的,您不应在这个问题上浪费太多时间:UML是一种OOA / OOD语言。汇总/组合通常最好推迟到OOP之后再决定。如果您尝试在UML模型中添加太多细节,则可能会使分析陷入瘫痪。
黑猩猩

13
+1表示“不要在此上浪费太多时间”。那就是我需要听到的!
罗尼2012年

8
“不要在此上浪费太多时间”访调员为什么不理解这一点?
titogeo 2012年

95

根据经验: 在此处输入图片说明

class Person {
    private Heart heart;
    private List<Hand> hands;
}

class City {
    private List<Tree> trees;
    private List<Car> cars
}

在组成上(人物,心,手)中,“人物”被破坏后,“子对象”(心,手)将被破坏。

合计(城市,树,汽车)中,当破坏城市时,“子对象”(树,汽车)不会被破坏。

最重要的是,组成强调相互存在,总的来说,不需要此属性。


1
共同存在,好人B
jxramos

感谢您的努力。一个很好的解释,任何外行都可以理解。
Ravindran Kanniah '16

我遇到的最简单和最好的解释。
阿卡什·拉格夫

最好的解释:)
Xiabili

UML 2.5澄清了差异。请参阅第40页的方框。110.所以您的答案现在是错误的,
qwerty_so

51

组合和聚合是关联的类型。它们之间有着密切的联系,就编程而言,两者似乎没有太大的区别。我将尝试通过Java代码示例来解释这两者之间的区别

聚合:该对象存在于另一个对象之外,在外部创建,因此将其作为参数(例如)传递给构造函数。例如:人-汽车。汽车是在不同的上下文中创建的,然后成为个人财产。

// code example for Aggregation:
// reference existing HttpListener and RequestProcessor
public class WebServer {
  private HttpListener listener;
  private RequestProcessor processor;
  public WebServer(HttpListener listener, RequestProcessor processor) {
    this.listener = listener;
    this.processor = processor;
  }
}

组成:对象仅作为另一个的一部分存在,或仅在另一个内部有意义。例如:人–心。您不会创造一颗心,然后将其传递给一个人。相反,当创造人类时就创造了心脏。

// code example for composition:
// create own HttpListener and RequestProcessor
public class WebServer {
  private HttpListener listener;
  private RequestProcessor processor;
  public WebServer() {
    this.listener = new HttpListener(80);
    this.processor = new RequestProcessor(“/www/root”);
  }
}

这里以示例的方式进行说明,汇总和组成之间的区别


5
到目前为止最好的答案之一。整洁:)
Rohit Singh's

6
显示对象何时可以与(组成)或不(聚集)其他对象一起存在的最佳Java代码示例。
米哈尔DOBI多布然斯基

请,我想知道我们是否应该将final用于Composition?
Outreagous

36

合成意味着子对象与父对象共享寿命。聚合没有。例如,一个国际象棋棋盘是由国际象棋方块组成的-没有这些棋盘就不会真正存在国际象棋方块。但是,汽车是零件的集合体-如果当时汽车不是汽车的一部分,汽车尾气仍然是汽车尾气。


18

我学到的例子是手指到手。您的手由手指组成。它拥有它们。如果手死了,手指就死了。您不能“聚集”手指。您不能随便拿多余的手指,然后随意将它们从手上取下。

从设计的角度来看,这里的价值通常与对象寿命有关,正如另一位海报家所说。假设您有一个客户,并且他们有一个帐户。该帐户是客户的“组成”对象(至少在我能想到的大多数情况下)。如果删除客户,则该帐户本身没有价值,因此也将被删除。相反,在对象创建上通常是这样。由于帐户仅在客户的上下文中具有含义,因此您会在创建客户的过程中创建帐户(或者,如果懒惰地将其创建,则将成为某些客户交易的一部分)。

在设计中考虑哪些对象拥有(组成)其他对象与仅引用(汇总)其他对象的对象很有用。它可以帮助确定对象创建/清理/更新的责任。

就代码而言,通常很难说出来。代码中的大多数内容都是对象引用,因此所引用的对象是组成(拥有)还是聚合的可能并不明显。


15

令人惊讶的是许多混乱存在对之间的区别部分与整体 - 关联的概念聚集组成。主要问题是普遍的误解(甚至在专家软件开发人员和UML的作者之间),即组合的概念隐含着整体及其各个部分之间的生命周期依赖性,因此,如果没有整体就不可能存在这些部分。但是,这种观点忽略了以下事实:在某些情况下,还存在与不可共享的零件存在部分整体关联的情况,这些零件可以与整体分离,并在整体破坏中幸存下来。

在UML规范文档中,术语“组合物”的定义始终暗含不可共享的部分,但是尚不清楚“组合物”的定义特征是什么,什么仅仅是可选特征。即使在新版本(截至2015年)中,UML 2.5在尝试改进术语“组合”的定义后,仍然仍然模棱两可,并且未提供任何指导如何使用非组合模型建模部分整体关联可共享的部分,可将这些部分从整体上拆下并在整体破坏中幸存下来,而不是无法将部分拆下并与整体一起破坏的情况。他们说

如果删除复合对象,则随即删除作为对象的所有零件实例。

但同时他们也说

在删除复合对象之前,可以从复合对象中删除零件对象,因此不能将其作为复合对象的一部分删除。

这种混乱指向UML定义的不完整,它不考虑组件和组合之间的生命周期依赖性。因此,重要的是要了解如何通过为<< 不可分割的 >>组合引入UML构造型来增强UML定义,在这种组合中,组件无法从其组合中分离出来,因此,只要销毁其组合,就必须将其销毁。

1)组成

正如马丁·福勒(Martin Fowler)解释的那样,表征构图的主要问题是“一个对象只能是一个构图关系的一部分”。Geert Bellekens 在出色的博客文章UML Composition vs Aggregation vs Association中也对此进行了解释。除了组合物(具有排他的不可共享的零件)的定义特征之外,组合物还可能在复合物及其组件之间具有生命周期依赖性。实际上,有两种此类依赖项:

  1. 每当组件必须始终连接到合成物时,换句话说,当组件具有强制性合成物时(如在合成线的合成物侧“完全一个”多重性所表示的),则必须重新使用销毁(或重新附加到)另一个组合中,或在其当前组合被销毁时销毁。下图显示了Person和之间的组成,以举例说明Heart。当心脏所有者死亡时,心脏会被破坏或移植到另一个人。
  2. 每当一个组件无法从其组合中分离时,换句话说,当它不可分离时,那么,只有在此之后,才必须在销毁其组合时销毁该组件。与不可分割的一部分这样的组合物的一个例子是间的组合物PersonBrain

在此处输入图片说明

总而言之,生命周期依赖性仅适用于特定的合成情况,但一般而言并不适用,因此它们不是定义特征。

UML规范指出:“部件可以在删除复合实例之前从复合实例中删除,因此不能作为复合实例的一部分被删除。” 在Car- Engine组成示例中,如下图所示,很明显的情况是,在损坏汽车之前可以将发动机从汽车上拆下,在这种情况下,发动机不会被损坏并且可以重新使用。这是由构图线的合成侧的零或一多重性暗示的。

在此处输入图片说明

合成组合端的合成关联末端的多样性为1或0..1,这取决于组件是否具有强制合成(必须连接到合成)的事实。如果组件是不可分割的,则意味着它们具有强制组合。

2)汇总

集合是具有部分整体关系的预期含义的另一种特殊关联形式,其中整体的各个部分可以与其他整体共享。例如,我们可以对类DegreeProgram和之间的聚合进行建模Course,如下图所示,因为课程是学位课程的一部分,并且课程可以在两个或多个学位课程之间共享(例如,工程学位可以共享C具有计算机科学学位的编程课程)。

在此处输入图片说明

但是,具有可共享部分的聚合的概念实际上并没有多大意义,因此它对实现没有任何影响,因此许多开发人员宁愿不在类图中使用白色菱形,而只是对简单关联建模代替。UML规范说:“共享聚合的精确语义因应用程序区域和建模者而异”。

聚合在整个端的关联端的多重性可以是任意数量(*),因为一部分可以属于任意数量的整体或在任意数量的整体之间共享


2
构图与生命周期并不严格相关,但是在大多数现实世界中,生命周期是决定是否构图或聚集的主要动力。我在回答中做了对冲,说生命周期“经常”相关而不是总是相关。很好地注意到不需要生命周期,但是指出该视图是一个“主要问题”,错误(以漂亮的粗体显示)使我感到无益,并且不利于指出实际的使用注意事项。
克里斯·凯塞尔

我非常不同意。生命周期注意事项不能成为“是否构成或聚集的主要动机”,因为在很多关联情况下,它们都代表着某种形式的整体感觉(聚集或组成),因此与它们无关。每当关联的下限多重性大于0的结尾(对应于强制引用属性)时,您就会获得生命周期依赖项。
Gerd Wagner

9

用代码术语来说,组合通常建议包含对象负责创建component *的实例,而包含对象仅包含对其的长期引用。因此,如果父对象被取消引用并被垃圾回收,子对象也将被取消引用。

所以这段代码...

Class Order
   private Collection<LineItem> items;
   ...
   void addOrderLine(Item sku, int quantity){
         items.add(new LineItem(sku, quantity));
   }
}

建议LineItem是Order的组成部分-LineItem在其包含的订单之外不存在。但是Item对象不是按顺序构造的-即使商店没有订单,它们也会根据需要传递并继续存在。因此它们是关联的,而不是组件。

*容器负责实例化该组件,但实际上可能未调用new ...()本身-这是java,通常首先要经过一两个工厂!


0

其他答案中提供的概念图很有用,但我想分享我发现有帮助的另一点。

对于代码生成,源代码或关系数据库的DDL,我已经从UML取得了一些进展。在这里,我使用组合来表示表在我的代码中具有一个不可为空的外键(在数据库中)和一个不可为空的“父”(通常是“最终”)对象。我使用聚合,在这种情况下,我希望记录或对象能够作为“孤立对象”存在,不附加到任何父对象或由另一个父对象“采用”。

换句话说,我已经使用了组合符号作为一种简写形式来暗示为模型编写代码时可能需要的一些额外约束。


0

我喜欢的示例: 成分: 水是池塘的一部分。(池塘是水的成分。) 聚集: 池塘里鸭子和鱼(池塘里有鸭子和鱼)

如您所见,我将“ part-of”和“ has”加粗了,因为这两个短语通常可以指出类之间存在什么样的联系。

但是,正如其他人指出的那样,连接是组合还是聚集在很多情况下取决于应用程序。


但是部分和有术语混淆了某个时候。例如,一个Person类“具有”名称,因此显示为Person与名称具有聚合关系。实际上,是构成关系。为什么?当Person对象销毁时,名称也应销毁。术语“名字是人的一部分”听起来并不自然。
Asif Shahzad 2013年

0

很难在聚合关系和复合关系之间进行区分,但是我将举一些例子,我们有一个房屋和房间,在这里我们有一个复合关系,房间是房屋的一部分,并且开始了房间的生活关于房屋生活,当房屋生活结束时,威尔将结束,房间是房屋的一部分,我们谈论的是组成,例如国家和首都,书籍和书页。以总体关系为例,以团队和玩家为例,玩家可以没有团队而存在,而团队是一组玩家,玩家的生活可以在团队生活之前开始,如果我们谈论编程,我们可以创建玩家,然后我们将创建团队,但对于构图否,我们在房屋内部创建了房间。组成---->合成|组成。聚合------->组| 元件


0

让我们设置条件。聚合是UML标准中的一个元术语,表示合并和共享聚合,简称为 shared。通常,它被错误地称为“聚合”。它是BAD,因为组合也是聚合。据我了解,您的意思是“共享”。

与UML标准相比:

复合-表示属性是复合聚合的,即复合对象负责组成对象(部件)的存在和存储。

因此,大学到Cathedras协会是一个组成部分,因为Cathedra不存在于University(IMHO)之外

共享聚合的精确语义因应用程序区域和建模器而异。

即,如果您仅遵循自己或他人的某些原则,则可以将所有其他关联绘制为共享聚合。也看这里


0

考虑人体部分,例如肾脏,肝脏,大脑。如果我们尝试在此处映射组成和聚合的概念,则将类似于:

在诸如肾脏和肝脏的身体部位移植出现之前,这两个身体部位与人体组成,不能与人体隔离。

但是随着人体器官移植的出现,它们可以被移植到另一个人体中,因此这些部位与人体聚集在一起,因为它们现在可能与人体隔离存在。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.