Composition, Aggregation, and Association in Java – Java中的组合、聚合和关联

最后修改: 2019年 8月 3日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

Objects have relationships between them, both in real life and in programming. Sometimes it’s difficult to understand or implement these relationships.

对象之间有关系,无论是在现实生活中还是在编程中。有时,要理解或实现这些关系是很困难的。

In this tutorial, we’ll focus on Java’s take on three sometimes easily mixed up types of relationships: composition, aggregation, and association.

在本教程中,我们将重点介绍Java对三种有时容易混淆的关系类型的看法:组合、聚合和关联。

2. Composition

2.构成

Composition is a “belongs-to” type of relationship. It means that one of the objects is a logically larger structure, which contains the other object. In other words, it’s part or member of the other object.

组合是一种 “属于到 “的关系。它意味着其中一个对象是一个逻辑上更大的结构,它包含另一个对象。换句话说,它是另一个对象的一部分或成员。

Alternatively, we often call it a “has-a” relationship (as opposed to an “is-a” relationship, which is inheritance).

另外,我们通常称其为 “具有-a “关系(与 “是-a “关系相反,后者是继承关系)。

For example, a room belongs to a building, or in other words a building has a room. So basically, whether we call it “belongs-to” or “has-a” is only a matter of point of view.

例如,一个房间属于一个建筑,或者换句话说,一个建筑有一个房间。因此,基本上,我们称其为 “属于 “或 “拥有 “只是一个观点的问题。

Composition is a strong kind of “has-a” relationship because the containing object owns it. Therefore, the objects’ lifecycles are tied. It means that if we destroy the owner object, its members also will be destroyed with it. For example, the room is destroyed with the building in our previous example.

构成是一种强烈的 “有-A “关系,因为包含对象拥有它。因此,对象的生命周期是绑定的。这意味着,如果我们摧毁了所有者对象,其成员也将与之一起被摧毁。例如,在我们之前的例子中,房间与建筑一起被摧毁。

Note that doesn’t mean, that the containing object can’t exist without any of its parts. For example, we can tear down all the walls inside a building, hence destroy the rooms. But the building will still exist.

请注意,这并不意味着包含对象在没有任何部分的情况下就不能存在。例如,我们可以拆掉一栋楼里的所有墙壁,从而摧毁房间。但这栋楼仍然存在。

In terms of cardinality, a containing object can have as many parts as we want. However, all of the parts need to have exactly one container.

在cardinality方面,一个包含对象可以有我们想要的许多部分。但是,所有的部分都需要正好有一个容器

2.1. UML

2.1 UML

In UML, we indicate composition with the following symbol:

在UML中,我们用下面的符号表示组成。

composition

Note, that the diamond is at the containing object and is the base of the line, not an arrowhead. For the sake of clarity, we often draw the arrowhead too:

注意,钻石在包含对象处,是线条的底端,而不是箭头。为了清晰起见,我们通常也会画出箭头。

composition arrow

So, then, we can use this UML construct for our Building-Room example:

因此,我们可以将这个UML结构用于我们的Building-Room例子。

composition example

2.2. Source Code

2.2.源代码

In Java, we can model this with a non-static inner class:

在Java中,我们可以用一个非静态的内部类来建模。

class Building {
    class Room {}   
}

Alternatively, we can declare that class in a method body as well. It doesn’t matter if it’s a named class, an anonymous class or a lambda:

另外,我们也可以在方法主体中声明该类。无论它是一个命名的类、一个匿名的类还是一个lambda,都没有关系。

class Building {
    Room createAnonymousRoom() {
        return new Room() {
            @Override
            void doInRoom() {}
        };
    }

    Room createInlineRoom() {
        class InlineRoom implements Room {
            @Override
            void doInRoom() {}
        }
        return new InlineRoom();
    }
    
    Room createLambdaRoom() {
        return () -> {};
    }

    interface Room {
        void doInRoom();
    }
}

Note, that it’s essential, that our inner class should be non-static since it binds all of its instances to the containing class.

注意,至关重要的是,我们的内层类应该是非静态的,因为它把所有的实例都绑定到了包含类上。

Usually, the containing object wants to access its members. Therefore, we should store their references:

通常情况下,包含对象想要访问其成员。因此,我们应该存储它们的引用。

class Building {
    List<Room> rooms;
    class Room {}   
}

Note, that all inner class objects store an implicit reference to their containing object. As a result, we don’t need to store it manually to access it:

请注意,所有的内部类对象都存储了对其包含对象的隐式引用。因此,我们不需要手动存储它来访问它。

class Building {
    String address;
    
    class Room {
        String getBuildingAddress() {
            return Building.this.address;
        }   
    }   
}

3. Aggregation

3.聚合

Aggregation is also a “has-a” relationship. What distinguishes it from composition, that it doesn’t involve owning. As a result, the lifecycles of the objects aren’t tied: every one of them can exist independently of each other.

聚合也是一种 “具有-a “的关系。它与组合的不同之处在于,它不涉及所有权。因此,这些对象的生命周期并不相连:它们中的每一个都可以独立存在。

For example, a car and its wheels. We can take off the wheels, and they’ll still exist. We can mount other (preexisting) wheels, or install these to another car and everything will work just fine.

例如,一辆汽车和它的车轮。我们可以取下车轮,它们仍然存在。我们可以安装其他(预先存在的)车轮,或者将这些车轮安装到另一辆车上,一切都会正常工作。

Of course, a car without wheels or a detached wheel won’t be as useful as a car with its wheels on. But that’s why this relationship existed in the first place: to assemble the parts to a bigger construct, which is capable of more things than its parts.

当然,一辆没有轮子的汽车或脱离轮子的汽车不会像有轮子的汽车那样有用。但是,这就是为什么这种关系首先存在的原因:将零件组装成一个更大的构造,它能够比它的零件做更多的事情

Since aggregation doesn’t involve owning, a member doesn’t need to be tied to only one container. For example, a triangle is made of segments. But triangles can share segments as their sides.

由于聚合不涉及拥有,一个成员不需要只与一个容器绑定。例如,一个三角形是由线段组成的。但三角形可以共享线段作为其边。

3.1. UML

3.1 UML

Aggregation is very similar to composition. The only logical difference is aggregation is a weaker relationship.

聚合与构成非常相似。唯一的逻辑区别是聚合是一种较弱的关系。

Therefore, UML representations are also very similar. The only difference is the diamond is empty:

因此,UML的表示方法也非常相似。唯一的区别是钻石是空的。

aggregation

For cars and wheels, then, we’d do:

对于汽车和车轮,我们会做。

aggregation example

3.2. Source Code

3.2.源代码

In Java, we can model aggregation with a plain old reference:

在Java中,我们可以用一个普通的老引用来建立聚合模型。

class Wheel {}

class Car {
    List<Wheel> wheels;
}

The member can be any type of class, except a non-static inner class.

该成员可以是任何类型的类,除了非静态的内部类。

In the code snippet above both classes have their separate source file. However, we can also use a static inner class:

在上面的代码片段中,两个类都有其独立的源文件。然而,我们也可以使用一个静态的内部类。

class Car {
    List<Wheel> wheels;
    static class Wheel {}
}

Note that Java will create an implicit reference only in non-static inner classes. Because of that, we have to maintain the relationship manually where we need it:

请注意,Java只在非静态内层类中创建隐式引用。正因为如此,我们必须在我们需要的地方手动维护这种关系。

class Wheel {
    Car car;
}

class Car {
    List<Wheel> wheels;
}

4. Association

4.协会

Association is the weakest relationship between the three. It isn’t a “has-a” relationship, none of the objects are parts or members of another.

关联是三者之间最弱的关系。它不是一种 “有-a “的关系,没有一个对象是另一个对象的部分或成员。

Association only means that the objects “know” each other. For example, a mother and her child.

关联只意味着对象之间 “认识 “对方。例如,一个母亲和她的孩子。

4.1. UML

4.1 UML

In UML, we can mark an association with an arrow:

在UML中,我们可以用一个箭头标记一个关联。

association

If the association is bidirectional, we can use two arrows, an arrow with an arrowhead on both ends, or a line without any arrowheads:

如果关联是双向的,我们可以使用两个箭头,一个两端都有箭头的箭头,或者一个没有任何箭头的线。

association-bidirectional

We can represent a mother and her child in UML, then:

那么,我们可以用UML表示一个母亲和她的孩子。

association example

4.2. Source Code

4.2.源代码

In Java, we can model association the same way as aggregation:

在Java中,我们可以用与聚合相同的方式来模拟关联。

class Child {}

class Mother {
    List<Child> children;
}

But wait, how can we tell if a reference means aggregation or association?

但是,等等,我们如何判断一个引用是指聚合还是关联?

Well, we can’t. The difference is only logical: whether one of the objects is part of the other or not.

好吧,我们不能。区别只是在逻辑上:其中一个对象是否是另一个对象的一部分。

Also, we have to maintain the references manually on both ends as we did with aggregation:

此外,我们还必须在两端手动维护引用,就像我们在聚合时那样。

class Child {
    Mother mother;
}

class Mother {
    List<Child> children;
}

5. UML Sidenote

5.UML侧记

For the sake of clarity, sometimes we want to define the cardinality of a relationship on a UML diagram. We can do this by writing it to the ends of the arrow:

为了清楚起见,有时我们想在UML图上定义一个关系的cardinality。我们可以通过把它写在箭头的两端来做到这一点。

cardinality 1

Note, that it doesn’t make sense to write zero as cardinality, because it means there’s no relationship. The only exception is when we want to use a range to indicate an optional relationship:

注意,把0写成cardinality是没有意义的,因为它意味着没有关系。唯一的例外是当我们想用一个范围来表示一个可选的关系。

cardinality 2

Also note, that since in composition there’s precisely one owner we don’t indicate it on the diagrams.

还要注意的是,由于在构成中只有一个主人,所以我们没有在图上注明。

6. A Complex Example

6.一个复杂的例子

Let’s see a (little) more complex example!

让我们来看看一个(小)更复杂的例子!

We’ll model a university, which has its departments. Professors work in each department, who also has friends among each other.

我们将模拟一所大学,它有自己的部门。教授们在每个部门工作,他们之间也有朋友。

Will the departments exist after we close the university? Of course not, therefore it’s a composition.

我们关闭大学后,这些部门还会存在吗?当然不会,因此它是一种构成。

But the professors will still exist (hopefully). We have to decide which is more logical: if we consider professors as parts of the departments or not. Alternatively: are they members of the departments or not? Yes, they are. Hence it’s an aggregation. On top of that, a professor can work in multiple departments.

但教授们仍将存在(希望如此)。我们必须决定哪一个更符合逻辑:如果我们把教授视为部门的一部分或不。或者说:他们到底是不是系里的成员?是的,他们是。因此,它是一个聚合。除此之外,一个教授可以在多个部门工作。

The relationship between professors is association because it doesn’t make any sense to say that a professor is part of another one.

教授之间的关系是关联性的,因为说一个教授是另一个教授的一部分没有任何意义。

As a result, we can model this example with the following UML diagram:

因此,我们可以用下面的UML图来模拟这个例子。

complex example

And the Java code looks like this:

而Java代码看起来是这样的。

class University {
    List<Department> department;   
}

class Department {
    List<Professor> professors;
}

class Professor {
    List<Department> department;
    List<Professor> friends;
}

Note, that if we rely on the terms “has-a”, “belongs-to”, “member-of”, “part-of”, and so on, we can more easily identify the relationships between our objects.

请注意,如果我们依靠术语 “has-a”、”below-to”、”member-of”、”part-of”,我们可以更容易地识别我们的对象之间的关系。

7. Conclusion

7.结语

In this article, we saw the properties and representation of composition, aggregation, and association. We also saw how to model those relationships in UML and Java.

在这篇文章中,我们看到了组合、聚合和关联的属性和表现。我们还看到了如何在UML和Java中对这些关系进行建模。

As usual, the examples are available over on GitHub.

像往常一样,这些例子可以在GitHub上找到over