关联,聚合和组合之间有什么区别?
关联,聚合和组合之间有什么区别? 请在执行方面解释。
-
协会是所有对象都有自己的生命周期,没有所有者的关系。
我们举一个师生的例子。 多名学生可以和单身老师交往,而单身学生可以和多位老师交往,但对象之间没有所有权,都有自己的生命周期。 两者都可以独立创建和删除。
-
聚合是一种特殊的关联形式,所有对象都有自己的生命周期,但是所有权和子对象不能属于另一个父对象。
我们以部门和老师为例。 单个老师不能属于多个部门,但是如果我们删除部门,老师对象不会被销毁。 我们可以把它看作是一个“有”的关系。
-
合成也是聚合的特殊形式,我们可以称之为“死亡”关系。 这是一个强大的聚合类型。 子对象没有其生命周期,如果父对象被删除,所有的子对象也将被删除。
我们再来举一个房子和房间关系的例子。 房子可以包含多个房间 – 没有独立的房间生活,任何房间都不能属于两个不同的房子。 如果我们删除了房间,会自动删除。
我们再来看问题和选项之间的另一个关系。 单个问题可以有多个选项,选项不能属于多个问题。 如果我们删除问题,选项将自动被删除。
对于两个对象Foo
和Bar
,可以定义关系
协会 – 我与一个对象有关系。 Foo
使用Bar
public class Foo { void Baz(Bar bar) { } };
作品 – 我拥有一个物体,我负责它的一生,当Foo
去世, Bar
如此
public class Foo { private Bar bar = new Bar(); }
聚合 – 我有一个从别人借来的东西。 当Foo
去世时, Bar
可能继续活下去。
public class Foo { private Bar bar; Foo(Bar bar) { this.bar = bar; } }
我知道这个问题被标记为C#,但概念是非常普遍的问题,像这里重定向这里。 所以我将在这里提供我的观点(从Java的角度来看,我有点偏颇,在这里我更舒服)。
当我们想到面向对象的性质时,我们总是会想到对象,类(对象蓝图)以及它们之间的关系。 对象是通过方法相互关联和相互作用的。 换句话说,一个类的对象可以使用另一个类的对象提供的服务/方法。 这种关系被称为关联。 。
聚合和组合是关联的子集,意味着它们是特定的关联情况。
- 在一个类的聚合和组合对象中“拥有”另一个类的对象 。
- 但是有一个微妙的差别。 在构成中 ,拥有类的对象拥有的类的对象不能独立存在 (也称为“死亡关系”)。 它将始终作为其拥有对象的一部分,在Aggregation中 ,依赖对象是独立的,并且即使拥有类的对象已经死亡也可以存在。
- 所以在构成中如果拥有的对象是垃圾收集的话,拥有的对象也将是非聚合的情况。
困惑?
构图示例 :考虑一个汽车和一个非常特定于该汽车的发动机的例子(这意味着它不能在任何其他汽车中使用)。 Car和SpecificEngine类之间的这种类型的关系被称为Composition。 Car类的对象不能没有SpecificEngine类的对象,而没有Car类的SpecificEngine对象也没有意义。 简单地说,Car类只拥有SpecificEngine类。
聚合示例 :现在考虑类汽车和类轮 。 汽车需要一个Wheel对象来运作。 含义汽车对象自己的车轮对象,但我们不能说没有汽车对象的车轮对象没有意义。 它可以很好地用于自行车,卡车或不同的汽车对象。
总结起来 –
总而言之,关联是一个非常通用的术语,用于表示何时使用另一个类提供的功能。 如果一个父类对象拥有另一个子类对象,并且该子类对象在没有父类对象的情况下不能有意义地存在,我们就说它是组合。 如果可以的话就叫做聚合。
更多细节在这里。
从罗伯特·马丁在comp.object的一篇文章:
关联表示一个实例向另一个实例发送消息的能力。 这通常是通过指针或引用实例变量实现的,尽管它也可以作为方法参数实现,或者创建局部变量。
//[Example:] //|A|----------->|B| class A { private: B* itsB; };
聚合是典型的整体/部分关系。 这与一个关联完全相同,除了实例不能有循环聚合关系(即一个部分不能包含其全部)。
//[Example:] //|Node|<>-------->|Node| class Node { private: vector<Node*> itsNodes; };
这是聚合的事实意味着Node的实例不能形成一个循环。 因此,这是一个节点树,而不是一个节点图。
除了“部分”的生命周期由“整体”控制之外,构成完全像聚合。 这个控制可能是直接的或传递的。 也就是说,“整体”可以直接负责创造或者摧毁“部分”,也可以接受已经创造出来的部分,然后传递给承担责任的其他整体。
//[Example:] //|Car|<#>-------->|Carburetor| class Car { public: virtual ~Car() {delete itsCarb;} private: Carburetor* itsCarb };
正如其他人所说,关联是对象之间的关系,聚合和构成是关联的类型。
从实现的角度来看, 通过引用类成员来获得聚合。 例如,如果类A聚合了一个类B的对象,你将会有这样的东西(用C ++):
class A { B & element; // or B * element; };
聚合的语义是当一个对象A被销毁时,它所存储的B对象将仍然存在。 在使用构图时,你有更强的关系,通常通过按值存储成员:
class A { B element; };
这里,当一个A对象被销毁时,它所包含的B对象也将被销毁。 最简单的方法是按值存储成员,但也可以使用一些智能指针,或者在析构函数中删除成员:
class A { std::auto_ptr<B> element; }; class A { B * element; ~A() { delete B; } };
重要的一点是,在构图中,容器对象拥有所包含的一个,而在聚合中,它引用它。
依赖 (参考)
这意味着两个对象之间没有概念上的联系。 例如EnrollmentService对象引用Student&Course对象(作为方法参数或返回类型)
public class EnrollmentService { public void enroll(Student s, Course c){} }
协会 (has-a)
这意味着几乎总是有对象之间的联系(他们是相关的)。 Order对象有一个 Customer对象
public class Order { private Customer customer }
聚合 (有一个+整个部分)
两个对象之间存在全部关系的特殊类型的关联。 他们也许可以互相生存。
public class PlayList{ private List<Song> songs; }
注意:最棘手的部分是区分聚合与正常关联。 老实说,我认为这是不同的解释。
组成 (有一个+全部+所有权)
特殊的聚合。 Apartment
由一些Room
组成。 没有Apartment
Room
就不能存在。 当公寓被删除时,所有相关联的房间也被删除。
public class Apartment{ private Room bedroom; public Apartment() { bedroom = new Room(); } }
协会是广义的关系概念。 它包括组合和聚合。
组合 ( 混合 )是一种将简单对象或数据类型组合成更复杂的对象的方法。 组合是许多基本数据结构的重要组成部分
聚合 ( 集合 )不同于普通的组合,因为它并不意味着所有权。 在构图中,当拥有的对象被破坏时,被包含的对象也被破坏。 在汇总中,这不一定是正确的。
两者都表示客体之间的关系,只有不同的力量。
现在让我们观察下面的图像
关于三种关系概念关联 , 聚合和构成之间的区别存在多少混淆是令人惊讶的。
请注意,术语聚合和合成已经在C ++社区中使用过,可能在一段时间之后,它们被定义为UML类图中关联的特例。
主要的问题是广泛的和持续的误解(甚至在专家软件开发者之间):构图的概念意味着整体与其部分之间的生命周期依赖,使得部分不能在没有整体的情况下存在,忽略了也存在与非共同部分的部分 – 整体关联的情况下,部分可以脱离部分,并在整体的破坏中幸免于难。
据我所知,这种混乱有两个根源:
-
在C ++社区中,术语“聚合”用于定义用于引用另一独立类的对象的属性的类的意义上(参见例如[1]),这是UML类图中的关联的意义。 术语“组合”用于为其对象定义组件对象的类,这样在销毁组合对象时,这些组件对象也被销毁。
-
在UML类图中,“聚合”和“组合”都被定义为代表部分 – 整体关系(已经在哲学中讨论了很长时间)的关联的特例。 在他们的定义中,“聚合”和“组合”之间的区别是基于这样一个事实,即它允许共享两个或更多个整体之间的一部分。 他们将“作品”定义为不可共享(独占)的部分,而“聚合”可以共享其部分。 另外,他们还说了如下的东西:很多时候,但并不是所有的情况下,作品都会在整体和部分之间产生一种生命周期的依赖关系,这样,这些部分就不可能没有整体。
因此,虽然UML把“聚合”和“组合”这些术语置于正确的(部分 – 整体)关系的背景下,但他们并没有设法以清晰明确的方式来定义它们,捕捉开发者的直觉。 然而,这并不令人惊讶,因为这些关系可能有这么多不同的属性(和实现的细微差别),开发人员不会对如何实现这些关系感到不满意。
请参阅下面列出的2009年4月SO问题的扩展答案 。
并且假设在C ++社区中OOP对象之间定义了“组合”的属性(这个信念仍然被广泛保留):两个相关对象(组合和它的组件)之间的运行时生命周期依赖是对于“组合”来说并不是真正的特征,因为在其他类型的关联中,由于参照完整性我们也可以有这样的依赖关系。
例如,下面的代码模式为“合成”在SO回答中提出:
final class Car { private final Engine engine; Car(EngineSpecs specs) { engine = new Engine(specs); } void move() { engine.work(); } }
被访者声称,“组成”是没有其他类别可以参考/知道该组成部分的特征。 但是,对于“构成”的所有可能情况,这肯定不是真的。 特别是在汽车发动机的情况下,汽车的制造商可能需要在另一个等级的帮助下才能实施,因此在发生问题时可能需要引擎才能够联系汽车的所有者。
[1] http://www.learncpp.com/cpp-tutorial/103-aggregation/
附录 – 有关StackOverflow的组合与聚合的重复问题的不完整列表
[ 2009年4月 ]
聚合与组合 [主要基于意见而关闭]
[ 2009年4月 ]
组合和关系有什么区别? [ 2009年5月 ]
关联,聚合和组合之间的区别
[ 2009年5月 ]
构图和聚合有什么区别? [重复]
[ 2009年10月 ]
聚合,组合和依赖之间有什么区别? [标记为重复]
[ 2010年11月 ]
关联与聚合 [标记为重复]
[ 2012年8月 ]
Java中Aggregation和Composition之间的实现差异
[ 2015年2月 ]
UML – 关联或聚合(简单的代码片段)
协会
协会代表着两个类别之间的关系。它可以是单向的(单向的)或双向的(双向的)
例如:
- 单向
客户下订单
- 双向
A与B结婚
B与A结婚
聚合
聚合是一种关联。但具有特定的特征。聚合是一个较大的“整体”类中包含一个或多个较小的“部分”类的关系。相反,较小的“部分”类是“整体”较大类的一部分。
例如:
俱乐部有会员
一个俱乐部(“整体”)由几个俱乐部成员(“部分”)组成,会员有生活在俱乐部之外。 如果俱乐部(“整体”)死亡,成员(“部分”)不会死亡。 因为会员可以属于多个俱乐部(“整体”)。
组成
这是一种更强大的聚合形式,“整体”负责创造或破坏其“部分”
例如:
学校有部门
在这种情况下,学校(“整体”)将会死亡,部门(“部分”)将随之死亡。 因为每个部分只能属于一个“整体”。
理解为什么我们甚至应该使用不止一次的关系线是很重要的。 最显而易见的原因是要描述父类和子类之间的关系(当父母删除其所有孩子的时候,其结果被删除),但是更加无能为力的是,我们要区分简单关联和合成,以便对可见性向相关类传播变化,这对理解和降低系统复杂性起着重要作用。
协会
描述类之间静态关系的最抽象的方法是使用关联链接,这个关联链接简单地说明在两个或更多类之间存在某种链接或依赖关系。
弱关联
可以将ClassA链接到ClassB,以显示其方法之一包括ClassB实例的参数,或者返回ClassB的实例。
强大的协会
ClassA也可以链接到ClassB,以显示它拥有对ClassB实例的引用。
聚合(共享协会)
在ClassA(整体)和ClassB(部分)之间存在部分关系的情况下,我们可以更具体地使用聚合链接而不是关联链接,强调ClassB也可以由应用程序中的其他类聚合因此聚合也被称为共享关联)。
需要注意的是,聚合链接并没有以任何方式声明ClassA拥有ClassB,也没有声明在两者之间存在父子关系(因此父项被删除的所有子项被删除)。 其实,恰恰相反! 聚合链接通常用于强调ClassA不是ClassB的专用容器,因为实际上ClassB具有另一个容器。
聚集vs关联关联链接可以在任何情况下取代聚合链接,而聚合不能取代关联在类之间只有“薄弱环节”的情况下,也就是说,ClassA包含的方法包含ClassB的参数,但ClassA没有保持对ClassB实例的引用。
Martin Fowler认为聚合链接不应该被使用,因为它没有附加价值,并且干扰了一致性。引用Jim Rumbaugh“把它看作是一个模拟安慰剂”。
组成(非共享协会)
除了ClassA和ClassB之间的部分关系之外,我们应该更加具体地使用组合链接 – 两者之间存在强烈的生命周期依赖关系,这意味着当ClassA被删除时,ClassB也被删除
组合连接显示一个类(容器,整体)对其他类(部分)具有排他性的所有权,这意味着容器对象及其部分构成父子关系。
与关联和聚合不同,在使用组合关系时,组合类不能作为组合类的返回类型或参数类型出现。 因此,对组合类的更改不能传播到系统的其余部分。 因此,随着系统的发展,构成的使用限制了复杂性的增长。
测量系统复杂性
只需查看UML类图并评估关联,聚合和合成关系线即可测量系统复杂性。 衡量复杂性的方法是确定可以通过改变一个特定的类来影响多少个类。 如果A类公开了B类,那么任何使用A类的给定类在理论上都会受到B类变化的影响。系统中每个类可能受到影响的类的总数就是整个系统的复杂度。
你可以在这里阅读更多: http : //aviadezra.blogspot.com/2009/05/uml-association-aggregation-composition.html
两个对象之间的关系被称为关联 。
一个对象拥有另一个对象时,一个关联被称为构图 。
当一个对象使用另一个对象时,关联被称为聚合 。
这些答案的问题在于他们是一半的故事:他们解释说,聚集和组合是联合的形式,但是他们没有说联合是否有可能不是这样。
我根据对SO上的许多帖子的简短阅读来收集一些UML文档,其中有四种主要的阶级联合的具体形式:
- 组成:A是一个B组成的; B没有A就不存在,就像家里的房间一样
- 聚合:A有一个B; B可以没有A,像教室里的学生一样存在
- 依赖关系:A uses-a B; A和B之间不存在生命周期依赖关系,如方法调用参数,返回值或在方法调用期间创建的临时对象
- 概括:A是一个B
当两个实体之间的关系不是其中之一时,它可以被称为一个通用意义上的“关联”,并进一步描述其他方式(注释,刻板印象等)。
我的猜测是“通用关联”主要用于两种情况:
- 当一个关系的细节仍在制定之中时; 图表中的这种关系应该尽快地转换成实际的/将要成为的(其他4种之一)。
- 当关系与UML预定的4个关系中的任何一个不匹配时; “泛型”协会仍然给你一种表示“不是其他人之一”的关系的方式,这样你就不会陷入与注释中的不正确关系“这实际上并不是聚合,它只是UML没有任何其他符号我们可以使用“
我想这个链接会做你的功课: http : //ootips.org/uml-hasa.html
为了理解这些术语,我记得在编程初期的一个例子:
如果你有一个“棋盘”对象,其中包含“框”对象的组成,因为如果删除了“国际象棋棋盘”,没有理由让盒子存在了。
如果你有一个“方形”的对象有一个“颜色”的对象和方块被删除的“颜色”对象可能仍然存在,这是聚合
他们都是联想 ,主要的区别是概念上的
了解关联 , 聚合 , 组合和其他UML类关系可以在这里找到理解UML类关系
我想说明如何在Rails中实现这三个术语。 ActiveRecord调用两个模型之间的任何类型的关系association
。 在阅读与ActiveRecord相关的文档或文章时,不会经常发现术语composition
和aggregation
。 通过将关联类宏中的一个添加到类的主体来创建关联。 其中一些宏是belongs_to
, has_one
, has_many
等。
如果我们想要设置一个composition
或aggregation
,我们需要将belongs_to
添加到拥有的模型(也称为child),将has_one
或has_many
到拥有模型(也称为父类)。 我们设置composition
或aggregation
取决于我们传递给子模型中belongs_to
的选项。 在Rails 5之前,设置belongs_to
没有任何选项创建一个aggregation
,这个子可以没有父节点存在。 如果我们想要一个composition
,我们需要通过添加required: true
的选项来显式声明required: true
:
class Room < ActiveRecord::Base belongs_to :house, required: true end
在Rails 5中,这个改变了。 现在,声明一个belongs_to
关联会默认创建一个composition
,如果没有父对象,子对象就不能存在。 所以上面的例子可以被重写为:
class Room < ApplicationRecord belongs_to :house end
如果我们想允许子对象在没有父对象的情况下存在,我们需要通过optional
的选项明确地声明它
class Product < ApplicationRecord belongs_to :category, optional: true end
构图 :这是一旦你摧毁了一个物体(学校),另一个物体(教室)将被摧毁。 两者不能独立存在。
聚集 :这与上面( Composition
)关联完全相反,一旦你杀死一个对象( Company
),绑定到它的另一个对象( Employees
)可以自己存在。
协会 。
组合和聚合是两种形式的联系。