关于uml:关联,聚合和组合有什么区别?

What is the difference between association, aggregation and composition?

关联、聚合和组合之间有什么区别?请说明实施情况。


对于两个对象,可以定义FooBar之间的关系。

关联-我和一个对象有关系。Foo使用Bar

1
2
3
4
public class Foo {
    void Baz(Bar bar) {
    }
};

构图-我拥有一个对象,我对它的一生负责。当Foo死了,Bar也死了。

1
2
3
public class Foo {
    private Bar bar = new Bar();
}

聚合-我有一个从别人那里借来的对象。当Foo死后,Bar可以继续生活。

1
2
3
4
5
6
public class Foo {
    private Bar bar;
    Foo(Bar bar) {
       this.bar = bar;
    }
}


我知道这个问题被标记为C,但是这些概念是非常普通的问题,比如这里的重定向。因此,我将在这里提供我的观点(有点偏向于Java的观点,我觉得更舒服)。

当我们想到面向对象的本质时,我们总是想到对象、类(对象蓝图)以及它们之间的关系。对象通过方法相互关联和交互。换句话说,一个类的对象可以使用另一个类的对象提供的服务/方法。这种关系被称为联系。

聚合和组合是关联的子集,这意味着它们是关联的特定情况。

enter image description here

  • 在一个类的聚合和组合对象中,"拥有"另一个类的对象。
  • 但是有一个微妙的区别。在组合中,属于其所属类的对象所拥有的类的对象不能独立生活(也称为"死亡关系")。它总是作为拥有对象的一部分存在,在聚合中,依赖对象是独立的,即使拥有类的对象是死的,也可以存在。
  • 因此,在组合中,如果所属对象被垃圾收集,则所属对象在聚合中也是如此。

困惑的?

组成示例:考虑一辆汽车和一个发动机的例子,它非常具体的汽车(这意味着它不能在任何其他汽车中使用)。汽车和特定引擎类之间的这种关系称为组合。没有特定引擎类的对象,汽车类的对象就不存在;没有汽车类,特定引擎的对象就没有意义。简单来说,汽车类只"拥有"特定的引擎类。

聚合示例:现在考虑类车和类轮。汽车需要一个轮子物体来工作。这意味着汽车对象拥有车轮对象,但我们不能说没有汽车对象,车轮对象就没有意义。它可以很好地用于自行车,卡车或不同的汽车对象。

总结一下-

总而言之,关联是一个非常通用的术语,用来表示一个类何时使用另一个类提供的功能。如果一个父类对象拥有另一个子类对象,并且没有父类对象,子类对象就不能有意义地存在,那么我们就说它是组合。如果可以,则称为聚合。

更多详细信息。我是http://opensourceforgeks.blogspot.in的作者,并在上面添加了一个链接到相关文章以获取更多上下文。


关联是关系的广义概念。它包括组成和聚合。

组合(混合)是将简单对象或数据类型包装为单个单元的一种方法。组合是许多基本数据结构的关键组成部分。

聚合(集合)不同于普通组合,因为它并不意味着所有权。在组合中,当所属对象被销毁时,所包含的对象也会被销毁。在聚合中,这不一定是正确的。

Both denotes relationship between object and only differ in their strength.

记住区别的诀窍:有一个-聚合和自己的-复合

氧化镁

现在让我们观察下图

氧化镁

氧化镁

类比:

合成:下图是图像合成,即使用单个图像生成一个图像。氧化镁

聚合:在单个位置收集图像

enter image description here

例如,一所大学拥有多个系,每个系都有许多教授。如果大学关闭,这些系将不再存在,但这些系的教授将继续存在。因此,大学可以看作是一个部门的组成部分,而部门则是教授的集合。此外,教授可以在多个系工作,但一个系不能是多所大学的一部分。


依赖项(引用)这意味着两个对象之间没有概念上的联系。例如,EnrollmentService对象引用学生和课程对象(作为方法参数或返回类型)

1
2
3
public class EnrollmentService {
    public void enroll(Student s, Course c){}
}

协会(HAS-A)这意味着对象之间几乎总是有一个链接(它们是相关联的)。订单对象具有客户对象

1
2
3
public class Order {
    private Customer customer
}

聚合(has-a+整体部分)两个对象之间存在整体部分关系的特殊类型的关联。但是他们可能没有彼此生活。

1
2
3
public class PlayList{
    private List<Song> songs;
}

注意:最棘手的部分是区分聚合和正常关联。老实说,我认为这是开放的不同解释。

组成(有A+全部+所有权)特殊类型的聚合。Apartment由一些Rooms组成。没有Apartment就不能存在Room。删除公寓后,所有关联的房间也将被删除。

1
2
3
4
5
6
public class Apartment{
    private Room bedroom;
    public Apartment() {
       bedroom = new Room();
    }
}


从Robert Martin在comp.object中的一篇文章中:

关联表示一个实例向另一个实例发送消息的能力。这通常是用指针或引用实例变量实现的,尽管它也可以作为方法参数实现,或者创建局部变量。

1
2
3
4
5
6
7
8
9
//[Example:]

//|A|----------->|B|

class A
{
  private:
    B* itsB;
};

聚合[…]是典型的整体/部分关系。这与关联完全相同,只是实例不能具有循环聚合关系(即部分不能包含其整体)。

1
2
3
4
5
6
7
8
9
//[Example:]

//|Node|<>-------->|Node|

class Node
{
  private:
    vector<Node*> itsNodes;
};

这是聚合的事实意味着节点的实例不能形成循环。因此,这是一个节点树,而不是节点图。

组合[…]与聚合完全相同,只是"部分"的生存期由"整体"控制。此控件可以是直接的或可传递的。也就是说,"整体"可能对"部分"的创建或破坏负有直接责任,也可能接受已经创建的部分,然后将其传递给其他承担责任的整体。

1
2
3
4
5
6
7
8
9
10
11
//[Example:]

//|Car|<#>-------->|Carburetor|

class Car
{
  public:
    virtual ~Car() {delete itsCarb;}
  private:
    Carburetor* itsCarb
};


正如其他人所说,关联是对象之间的关系,聚合和组合是关联的类型。

从实现的角度来看,聚合是通过引用类成员来获得的。例如,如果类A聚集类B的对象,则会有类似这样的内容(C++):

1
2
3
4
class A {
    B & element;
  // or B * element;
};

聚合的语义是当一个对象A被破坏时,它所存储的B对象仍然存在。使用组合时,通常通过按值存储成员,可以获得更强的关系:

1
2
3
class A {
    B element;
};

这里,当一个对象被破坏时,它所包含的B对象也将被破坏。实现这一点的最简单方法是按值存储成员,但是您也可以使用一些智能指针,或者删除析构函数中的成员:

1
2
3
4
5
6
7
8
9
10
11
class A {
    std::auto_ptr element;
};

class A {
    B * element;

    ~A() {
        delete B;
    }
};

重要的一点是,在组合中,容器对象拥有所包含的对象,而在聚合中,它引用它。


协会

关联表示两个类之间的关系。它可以是单向(单向)或双向(双向)

例如:

  • 单向的
  • Customer places orders

  • 双向
  • A is married to B

    B is married to A

    聚合

    聚合是一种关联,但具有特定的特性。聚合是一个较大的"整体"类中包含一个或多个较小的"部分"类的关系。相反,较小的"部分"类是"整体"较大类的一部分。

    例如:

    club has members

    俱乐部("整体")由几个俱乐部成员("部分")组成。成员在俱乐部外有生命。如果俱乐部("整体")死亡,会员("部分")也不会随之死亡。因为会员可以属于多个俱乐部("整体")。

    组成

    这是一种更强大的聚合形式。"整体"负责其"部分"的创建或破坏。

    例如:

    A school has departments

    在这种情况下,学校("整体")将会死亡,部门("部分")也会随之死亡。因为每个部分只能属于一个"整体"。


    理解为什么我们要费心使用不止一次的关系线是很重要的。最明显的原因是描述类之间的父子关系(结果是父类删除了所有子类),但更不重要的是,我们希望区分简单的关联和组合,以便对相关类的更改的可见性和传播进行隐式限制,这是一个重要的问题。在理解和降低系统复杂性方面起着重要作用。

    Association

    描述类之间静态关系的最抽象的方法是使用关联链接,它只说明两个或更多类之间存在某种链接或依赖关系。

    弱关联

    ClassA可以链接到ClassB,以表明它的一个方法包含ClassB实例的参数,或者返回ClassB实例。

    强大的联系

    ClassA也可以链接到ClassB,以显示它包含对ClassB实例的引用。

    Aggregation (Shared Association)

    在类A(整体)和类B(部分)之间存在部分关系的情况下,我们可以更具体地使用聚合链接而不是关联链接,强调类B也可以由应用程序中的其他类聚合(因此聚合也称为共享关联)。

    enter image description here

    需要注意的是,聚合链接不会以任何方式声明ClassA拥有ClassB,也不会在这两者之间存在父子关系(当父级删除了其所有子级时,结果会删除其所有子级)。事实上,恰恰相反!聚合链接通常用于强调ClassA不是ClassB的独占容器,因为实际上ClassB有另一个容器。

    聚合V.S.关联关联链接可以在任何情况下替换聚合链接,而聚合不能在类之间只有"弱链接"的情况下替换关联,即类A具有包含类B参数但类A不包含对类B实例的引用的方法/S。

    MartinFowler建议根本不应该使用聚合链接,因为它没有附加值,并且会干扰一致性,引用JimRumbaugh的话,"把它当作一个模型安慰剂"。

    Composition (Not-Shared Association)

    我们应该更具体一些,并在以下情况下使用组合链接:除了ClassA和ClassB之间的部分关系之外,两者之间还存在着强烈的生命周期依赖关系,这意味着当ClassA被删除时,ClassB也会因此被删除

    氧化镁

    composition链接显示类(container,whole)对其他类(parts)具有独占所有权,这意味着容器对象及其部分构成父子关系。

    与关联和聚合不同,使用组合关系时,组合类不能显示为复合类的返回类型或参数类型。因此,对组合类的更改不能传播到系统的其余部分。因此,随着系统的增长,组合的使用限制了复杂性的增长。

    Measuring system complexity

    通过查看UML类图并评估关联、聚合和组合关系线,可以简单地测量系统复杂性。度量复杂性的方法是确定更改一个特定的类可以影响多少类。如果类A公开了类B,那么任何使用类A的给定类理论上都会受到对类B的更改的影响。系统中每个类的潜在影响类的数量之和就是总的系统复杂性。

    你可以在我的博客上阅读更多:http://aviadezra.blogspot.com/2009/05/uml-association-aggregation-composition.html


    令人惊讶的是,对于关联、聚合和组合这三个关系概念之间的区别,存在着多少混淆。好的。

    注意,术语聚合和组合已经在C++社区中使用,可能在它们被定义为UML类图中的关联的特殊情况之前一段时间。好的。

    主要的问题是普遍存在的误解(即使是在专家软件开发人员中),即组合的概念意味着整体与其部分之间的生命周期依赖性,以至于部分不能没有整体而存在,忽略了部分整体与不可共享部分之间也存在关联的情况。在这里,部分可以从整体中分离出来,并在整体的破坏中幸存下来。好的。

    据我所知,这种混乱有两个根源:好的。

  • 在C++社区中,术语"聚合"用于定义一个引用另一个独立类对象的属性的类(参见,例如,[1)],这是UML类图中的关联感。术语"composition"用于为其对象定义组件对象的类,这样在销毁复合对象时,这些组件对象也将被销毁。好的。

  • 在UML类图中,"聚合"和"组合"都被定义为表示部分-整体关系的关联的特殊情况(在哲学中已经讨论了很长时间)。在他们的定义中,"聚合"和"组合"之间的区别是基于这样一个事实,即如果它允许在两个或多个整体之间共享一个部分。他们将"组合"定义为具有不可共享(独占)的部分,"聚合"可以共享其部分。此外,他们还说了如下的话:通常情况下,但并非所有情况下,构图都在整体及其部分之间具有生命周期依赖性,因此,没有整体,部分就不可能存在。好的。

  • 因此,尽管UML已经将术语"聚合"和"组合"放在了正确的上下文中(部分整体关系),但它们没有设法以清晰、明确的方式定义它们,捕获开发人员的直觉。然而,这并不令人惊讶,因为这些关系可以有这么多不同的属性(和实现的细微差别),并且开发人员不会对如何实现它们进行攻击。好的。

    另请参阅下面列出的我对2009年4月的SO问题的扩展回答。好的。

    以及假定在C++社区中OOP对象之间定义"组成"的属性(并且这个信念仍然被广泛持有):在两个相关对象(复合及其组件)之间的运行时生命周期依赖性,对于"构图"而言并不是真正的特征,因为我们可以由于引用内特而具有这样的依赖关系。格里蒂也在其他类型的协会。好的。

    例如,在SO答案中提出了以下"组合"的代码模式:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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月]
    JavaBR/>中聚合与构图的实现差异[2015年2月]
    UML-关联或聚合(简单代码段)好的。好啊。


    组合(如果删除"整体","部分"也会自动删除-"所有权")。

    • 在新类中创建现有类的对象。这称为组合,因为新类由现有类的对象组成。

    • 通常使用普通成员变量。

    • 如果合成类自动处理负责创建/销毁子类的分配/释放,则可以使用指针值。

    enter image description here

    C++中的构图

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    #include <iostream>
    using namespace std;
    /********************** Engine Class ******************/
    class Engine
    {
        int nEngineNumber;
        public:
        Engine(int nEngineNo);
        ~Engine(void);
    };
    Engine::Engine(int nEngineNo)
    {
        cout<<" Engine :: Constructor" <<endl;
    }
    Engine::~Engine(void)
    {
        cout<<" Engine :: Destructor" <<endl;
    }
    /********************** Car Class ******************/
    class Car
    {
        int nCarColorNumber;
        int nCarModelNumber;
        Engine objEngine;
        public:
        Car (int, int,int);
        ~Car(void);
    };
    Car::Car(int nModelNo,int nColorNo, int nEngineNo):
    nCarModelNumber(nModelNo),nCarColorNumber(nColorNo),objEngine(nEngineNo)
    {
        cout<<" Car :: Constructor" <<endl;
    }
    Car::~Car(void)
    {
        cout<<" Car :: Destructor" <<endl;
        Car
        Engine
        Figure 1 : Composition
    }
    /********************** Bus Class ******************/
    class Bus
    {
        int nBusColorNumber;
        int nBusModelNumber;
        Engine* ptrEngine;
        public:
        Bus(int,int,int);
        ~Bus(void);
    };
    Bus::Bus(int nModelNo,int nColorNo, int nEngineNo):
    nBusModelNumber(nModelNo),nBusColorNumber(nColorNo)
    {
        ptrEngine = new Engine(nEngineNo);
        cout<<" Bus :: Constructor" <<endl;
    }
    Bus::~Bus(void)
    {
        cout<<" Bus :: Destructor" <<endl;
        delete ptrEngine;
    }
    /********************** Main Function ******************/
    int main()
    {
        freopen ("InstallationDump.Log","w", stdout);
        cout<<"--------------- Start Of Program --------------------"<<endl;
        // Composition using simple Engine in a car object
        {
            cout<<"------------- Inside Car Block ------------------"<<endl;
            Car objCar (1, 2,3);
        }
        cout<<"------------- Out of Car Block ------------------"<<endl;
        // Composition using pointer of Engine in a Bus object
        {
            cout<<"------------- Inside Bus Block ------------------"<<endl;
            Bus objBus(11, 22,33);
        }
        cout<<"------------- Out of Bus Block ------------------"<<endl;
        cout<<"--------------- End Of Program --------------------"<<endl;
        fclose (stdout);
    }

    产量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    --------------- Start Of Program --------------------
    ------------- Inside Car Block ------------------
    Engine :: Constructor
    Car :: Constructor
    Car :: Destructor
    Engine :: Destructor
    ------------- Out of Car Block ------------------
    ------------- Inside Bus Block ------------------
    Engine :: Constructor
    Bus :: Constructor
    Bus :: Destructor
    Engine :: Destructor
    ------------- Out of Bus Block ------------------
    --------------- End Of Program --------------------

    聚合(如果删除"整体","部分"可能存在-"无所有权")。

    • 聚合是一种特定类型的组合,其中不隐含复杂对象和子对象之间的所有权。当一个集合被破坏时,子对象不会被破坏。

    • 通常使用指向位于聚合类范围之外的对象的指针变量/引用变量

    • 可以使用指向位于聚合类范围之外的对象的引用值

    • 不负责创建/销毁子类

    氧化镁

    C++中的聚合代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    #include <iostream>
    #include <string>
    using namespace std;
    /********************** Teacher Class ******************/
    class Teacher
    {
        private:
        string m_strName;
        public:
        Teacher(string strName);
        ~Teacher(void);
        string GetName();
    };
    Teacher::Teacher(string strName) : m_strName(strName)
    {
        cout<<" Teacher :: Constructor --- Teacher Name ::"<<m_strName<<endl;
    }
    Teacher::~Teacher(void)
    {
        cout<<" Teacher :: Destructor --- Teacher Name ::"<<m_strName<<endl;
    }
    string Teacher::GetName()
    {
        return m_strName;
    }
    /********************** Department Class ******************/
    class Department
    {
        private:
        Teacher *m_pcTeacher;
        Teacher& m_refTeacher;
        public:
        Department(Teacher *pcTeacher, Teacher& objTeacher);
        ~Department(void);
    };
    Department::Department(Teacher *pcTeacher, Teacher& objTeacher)
    : m_pcTeacher(pcTeacher), m_refTeacher(objTeacher)
    {
        cout<<" Department :: Constructor" <<endl;
    }
    Department::~Department(void)
    {
        cout<<" Department :: Destructor" <<endl;
    }
    /********************** Main Function ******************/
    int main()
    {
        freopen ("InstallationDump.Log","w", stdout);
        cout<<"--------------- Start Of Program --------------------"<<endl;
        {
            // Create a teacher outside the scope of the Department
            Teacher objTeacher("Reference Teacher");
            Teacher *pTeacher = new Teacher("Pointer Teacher"); // create a teacher
            {
                cout<<"------------- Inside Block ------------------"<<endl;
                // Create a department and use the constructor parameter to pass the teacher to it.
                Department cDept(pTeacher,objTeacher);
                Department
                Teacher
                Figure 2: Aggregation
            } // cDept goes out of scope here and is destroyed
            cout<<"------------- Out of Block ------------------"<<endl;
            // pTeacher still exists here because cDept did not destroy it
            delete pTeacher;
        }
        cout<<"--------------- End Of Program --------------------"<<endl;
        fclose (stdout);
    }

    产量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    --------------- Start Of Program --------------------
    Teacher :: Constructor --- Teacher Name :: Reference Teacher
    Teacher :: Constructor --- Teacher Name :: Pointer Teacher
    ------------- Inside Block ------------------
    Department :: Constructor
    Department :: Destructor
    ------------- Out of Block ------------------
    Teacher :: Destructor --- Teacher Name :: Pointer Teacher
    Teacher :: Destructor --- Teacher Name :: Reference Teacher
    --------------- End Of Program --------------------


    这些答案的问题在于,它们只是故事的一半:它们解释了聚合和组合是关联的形式,但它们没有说明关联是否可能既不是这两种形式。

    我收集了一些关于so的许多文章的简要阅读资料和一些UML文档,这些文档有4种主要的具体类关联形式:

  • 组成:A由A B组成;B没有A就不存在,就像家里的房间一样。
  • 聚合:A有A有B;B没有A就可以存在,就像课堂上的学生一样。
  • 依赖项:a使用a b;a和b之间没有生命周期依赖项,例如方法调用参数、返回值或方法调用期间创建的临时项
  • 概括:A是-A是B
  • 当两个实体之间的关系不是其中之一时,就可以用这个术语的一般意义称之为"关联",并进一步描述其他方式(注意、刻板印象等)。

    我猜"一般关联"主要用于两种情况:

    • 当一个关系的细节还在制定中时,应该尽快将图表中的这种关系转换为它的实际状态(另一个是4)。
    • 当一个关系与UML预先确定的4个关系中的任何一个都不匹配时,"通用"关联仍然为您提供了一种表示关系的方式,即"不是其他关系中的一个",这样您就不会使用不正确的关系和注释"这实际上不是聚合,只是UML没有我们可以使用的任何其他符号。""使用"


    我想这个链接可以帮你做功课:http://ootips.org/uml-hasa.html

    为了理解这些术语,我记得我早期编程时的一个例子:

    如果您有一个"棋盘"对象,其中包含组成的"框"对象,因为如果删除了"棋盘",则框将不再存在。

    如果有一个"square"对象有一个"color"对象,并且该平方被删除,则"color"对象可能仍然存在,即聚合

    两者都是关联,主要区别是概念上的


    组成:在这里,一旦你摧毁了一个物体(学校),另一个与之相连的物体(教室)也会被摧毁。两者都不能独立存在。

    聚合:这与上述(composition联想)完全相反,一旦你杀死了一个物体(Company,另一个物体(Employees)就可以单独存在。

    协会。组合和聚合是两种关联形式。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
        Simple rules:
        A"owns" B = Composition : B has no meaning or purpose in the system
        without A
        A"uses" B = Aggregation : B exists independently (conceptually) from A
        A"belongs/Have" B= Association; And B exists just have a relation
        Example 1:

        A Company is an aggregation of Employees.
        A Company is a composition of Accounts. When a Company ceases to do
        business its Accounts cease to exist but its People continue to exist.
        Employees have association relationship with each other.

        Example 2: (very simplified)
        A Text Editor owns a Buffer (composition). A Text Editor uses a File
        (aggregation). When the Text Editor is closed,
        the Buffer is destroyed but the File itself is not destroyed.

    我想说明这三个术语是如何在Rails中实现的。ActiveRecord调用两个模型之间的任何类型的关系:association。在阅读与ActiveRecord相关的文档或文章时,人们不会经常发现术语compositionaggregation。关联是通过将关联类宏之一添加到类的主体来创建的。其中一些宏是belongs_tohas_onehas_many等。

    如果我们想建立一个compositionaggregation,我们需要将belongs_to添加到拥有的模型(也称为子模型)中,并将has_onehas_many添加到拥有的模型(也称为父模型)中。我们是否建立了compositionaggregation,取决于我们传递给belongs_to调用子模型的选项。在Rails 5之前,在没有任何选项的情况下设置belongs_to,创建aggregation,子级可以不带父级而存在。如果我们想要一个composition,我们需要通过添加选项required: true来明确声明:

    1
    2
    3
    class Room < ActiveRecord::Base
      belongs_to :house, required: true
    end

    在Rails 5中,这被更改了。现在,声明一个belongs_to关联会创建一个composition,默认情况下,没有父节点子节点就不能存在。所以上面的例子可以重写为:

    1
    2
    3
    class Room < ApplicationRecord
      belongs_to :house
    end

    如果我们想让子对象在没有父对象的情况下存在,我们需要通过选项optional显式声明它。

    1
    2
    3
    class Product < ApplicationRecord
      belongs_to :category, optional: true
    end

    在一个非常简单的句子中:聚合和组合是关联的子集。

    • A使用B->这是一个聚合

    • A需要B->是合成。

    阅读更多信息。