聚合与组合

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

编辑:我所要求的部分原因是因为我们在工作中做了反向文档活动。 我们已经编写了代码,但是我们需要返回并为代码创build类图。 我们只是想正确地捕捉这些关联。

聚合和组成之间的区别取决于上下文。

以另一个答案中提到的汽车示例为例,是的,汽车尾气可以“独立”站立,因此可能不会与汽车构成 – 但这取决于应用。 如果你build立一个真正需要处理独立汽车尾气的应用程序(一个汽车商店pipe理应用程序?),聚合将是你的select。 但是,如果这是一个简单的赛车游戏,汽车尾气只能作为汽车的一部分 – 好吧,组成会相当好。

棋盘? 同样的问题。 只有在某些应用中,没有棋盘的棋子是不存在的。 在其他人(如玩具制造商)中,棋子肯定不能组成棋盘。

尝试将组合/聚合映射到您最喜欢的编程语言时,情况会变得更糟。 在某些语言中,差异可能更容易被注意到(事情简单时“通过引用”与“按价值”),但在其他语言中可能完全不存在。

还有最后一句忠告? 不要在这个问题上浪费太多时间。 这不值得。 这种区分在实践中几乎没有用(即使你有一个完全清晰的“组合”,你可能仍然想要实现它作为一个聚合由于技术原因,例如,caching)。

根据经验: 在这里输入图像说明

class Person { private Heart heart; private List<Hand> hands; } class City { private List<Tree> trees; private List<Car> cars } 

在构图 (人,心,手)中,一旦人被销毁,“子物体”(心,手)将被破坏。

在聚合 (城市,树,车)时,当城市被毁坏时,“子对象”(树,车)不会被销毁。

底线是,组成强调相互存在,并在聚合,这个属性是不需要的。

构图意味着子对象与父对象共享一个使用期限。 聚合不。 例如,国际象棋棋盘是由国际象棋棋盘组成的 – 棋盘没有棋盘就不存在。 但是,汽车是零部件的集合 – 汽车排气仍然是汽车排气,如果它不是当时的汽车的一部分。

组合和聚合是关联的types。 他们是非常密切相关的,在编程方面没有太大的差别。 我将尝试通过java代码示例来解释这两者之间的区别

聚合:对象存在于另一个之外,在外部创build,所以它被作为参数(例如)传递给构造者。 例如:人 – 车。 汽车是在不同的背景下创造的,而不是成为一个人的财产。

构图:物体只存在于另一物体中,或者只在另一物体中有意义,作为另一物体的一部分。 例如:人 – 心脏。 你没有创造一颗心,而是把它传递给一个人。 // WebServer由HttpListener和RequestProcessor聚合而成

 public class WebServer { private HttpListener listener; private RequestProcessor processor; public WebServer(HttpListener listener, RequestProcessor processor) { this.listener = listener; this.processor = processor; } } 

编写代码示例

// WebServer是HttpListener和RequestProcessor的组合,并控制它们的生命周期

 public class WebServer { private HttpListener listener; private RequestProcessor processor; public WebServer() { this.listener = new HttpListener(80); this.processor = new RequestProcessor(“/www/root”); } } 

这里用一个示例聚合和组合之间的区别来解释

我学到的例子是手指。 你的手是由手指组成的。 它拥有它们。 如果手死了,手指就死了。 你不能“聚合”手指。 你不能只是抓住额外的手指,随意附着和分开。

另一个海报说,从devise的angular度来看,这里的价值常常与对象的寿命有关。 假设你有一个客户,他们有一个帐户。 这个账户是客户的“组成”客体(至less在我能想到的大多数情况下)。 如果您删除了客户,那么账户本身没有任何价值,所以也会被删除。 对象创build往往是相反的。 由于帐户只在客户的上下文中有意义,因此您将创build帐户作为客户创build的一部分(或者,如果您懒洋洋地做,它将成为某些客户交易的一部分)。

在devise中考虑对象拥有(组合)其他对象与引用(聚合)其他对象的对象是有用的。 它可以帮助确定对象创build/清理/更新的责任。

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

关于部分 – 全部关联概念聚合组合之间的区别存在多less混淆是令人惊讶的。 主要的问题是广泛的误解(甚至在专家软件开发人员和UML的作者之间),构成的概念意味着整体和部分之间的生命周期依赖性,使得部分不能在没有整体的情况下存在。 但是这种观点却忽视了这样一个事实,即也存在着与非共同部分的部分 – 整体结合的情况,在这些部分中,部分可以脱离,并且可以在整体的破坏中生存。

在UML规范文档中,术语“组合”一词的定义一直暗含着不可共享的部分,但“组成”的定义特征是什么,以及什么仅仅是可选的特征还不清楚。 即使在新版本(截至2015年),UML 2.5在尝试改进术语“组合”的定义之后仍然是模棱两可的,并没有提供任何指导,可分离的零件,零件可以从零件中分离出来,并在整个零件的破坏中幸免于难,零件不能被分离并与整体一起销毁。 他们说

如果一个复合对象被删除,它的所有对象的部分实例都将被删除。

但同时他们也说

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

这种混淆意味着UML定义的不完整性,它不考虑组件和组件之间的生命周期依赖关系。 因此,理解UML定义如何通过引入“ 不可分割 ”组合的UML构造型来增强UML定义是非常重要的,在这种构造型中,组件不能从其组合中分离出来,因此,只要组合元素被破坏,就不得不销毁它们。

1)组成

正如马丁·福勒(Martin Fowler)所解释的 ,表征构图的主要问题是“一个对象只能是一个构图关系的一部分”。 这也可以在Gert Bellekens的优秀的博客文章UML Composition vs Aggregation vs Association中得到解释。 除了这种构图的特征(具有排他的不可共享的部分)之外,构图还可以在构图和构件之间具有生命周期依赖性。 实际上,这种依赖关系有两种:

  1. 无论何时一个组件必须总是被附加到一个组合上,或者换句话说,当它具有一个强制组合时 ,在组合线的复合一侧用“正好一个”多重性表示,那么它必须被重新使用在(或重新连接)另一个复合体时,或者在其当前的复合体被销毁时被销毁。 下面的图表显示了PersonHeart之间的组合。 当一个人的主人死后,心脏就会被毁坏或移植到另一个人身上。
  2. 只要一个组件不能脱离它的组合,或者换句话说,当它是不可分割的 ,那么只有当组件被破坏时,组件才能被销毁。 PersonBrain之间的组合就是这样一个不可分割组成部分的例子。

在这里输入图像说明

总之,生命周期依赖性只适用于具体的构成案例,但总的来说,它们并不是一个决定性的特征。

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

在这里输入图像说明

复合端的组合关联结束的多重性为1或0..1,这取决于组件是否具有强制组合(必须附加到组合)的事实。 如果组件是不可分的 ,这意味着它们有一个强制的组合。

2)聚合

聚合是与部分 – 全部关系的预期意义相关联的另一种特殊forms,其中整体的部分可以与其他整体共享。 例如,我们可以在DegreeProgramCourse之间build立一个聚合模型,如下图所示,因为课程是学位课程的一部分,课程可以在两个或多个学位课程之间共享(例如,工程学位可以分享具有计算机科学学位的C程序devise课程)。

在这里输入图像说明

然而,具有可共享部分的聚合概念实际上并不意味着太多,因此它对实现没有任何影响,因此许多开发人员不希望在他们的类图中使用白色菱形,而只是build立一个普通的关联代替。 UML规范说:“共享聚合的精确语义因应用程序区域和build模者而异”。

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

在代码方面,组合通常表明包含对象负责创build组件*的实例,而包含对象只包含对它的长期引用。 所以如果父对象被取消引用和垃圾回收,那么孩子也是如此。

所以这个代码…

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

build议LineItem是Order的一个组件 – LineItems在它们的包含顺序之外不存在。 但是Item对象并不是按顺序构build的 – 即使商店没有订单,它们也会根据需要传递并继续存在。 所以它们是关联的,而不是组件。

*容器负责实例化组件,但实际上它可能并不实际调用新的…()本身 – 这是java,通常有一个或两个工厂要经过第一个!

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

我已经从UML中获得了一些代码生成,源代码或关系数据库的DDL。 在那里,我用组合来表示一个表有一个不可为空的外键(在数据库中)和一个不可空的“父”(通常是“final”)对象,在我的代码中。 我使用聚合,我打算logging或对象能够作为“孤儿”存在,不附加到任何父对象,或由另一个父对象“采用”。

换句话说,我使用组合符号作为速记来暗示为模型编写代码时可能需要的额外约束。

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

正如你所看到的,我已经加了“部分”和“有”,因为这两个短语通常可以指出类之间存在什么样的连接。

但正如其他人所指出的,连接是组合还是聚合的次数多less取决于应用程序。

在聚合关系和复合关系之间做出区分是非常困难的,但是我将举一些例子,我们有房子和房间,在这里我们有一个复合关系,房间是房子的一部分,房间的生活开始了和家庭生活在一起,在家庭生活结束的时候完成,房间是房子的一部分,谈论国家和资本,书和页的构成。 对于整体关系的例子来说,采取团队和玩家,玩家可以没有团队存在,团队是一组玩家,玩家的生命可以在团队生活之前开始,如果我们谈论编程,我们可以创build玩家,之后我们将创build团队,但是对于作品no,我们在房子里面创造房间。 构图—->复合|构图。 聚合——-> group | 元件

让我们来设置条款。 聚合是UML标准中的一个元语言,意味着两者的组合和共享聚合,简称为共享 。 往往它被错误地命名为“聚合”。 这是坏的,因为构图也是一个聚合。 据我所知,你的意思是“共享”。

进一步从UML标准:

复合 – 指示属性是合成的,即复合对象负责组合对象(零件)的存在和存储。

所以,大学教会协会是一个组成部分,因为大学不存在大教堂(恕我直言)

共享聚合的精确语义因应用领域和build模者而异。

也就是说,如果你只遵循你的或其他人的一些原则,所有其他的协会都可以被看作共享的聚合。 也看这里 。

考虑像肾,肝,脑等人体部位。 如果我们试图在这里映射构图和聚合的概念,那将是:

在肾脏,肝脏等器官移植出现之前,这两个身体部位与人体组成,不能与人体分离。

但是身体部位移植出现以后,可以移植到另外一个人体内,这些部位与人体是一体的,因为它们与人体分离的存在是可能的。