11. Java组成、聚合和关联
对象在现实生活中和编程中都有它们之间的关系。有时很难理解或实现这些关系。
在本教程中,我们将重点关注Java对三种有时容易混淆的关系类型的看法:组合、聚合和关联。
1.组成
组成是一种“属于”的关系类型。这意味着其中一个对象是逻辑上更大的结构,其中包含另一个对象。换句话说,它是另一个对象的一部分或成员。
或者,我们通常称其为“has-a”关系(而不是“is-a”关系,即继承)。
例如,一个房间属于一个建筑,或者换句话说,一个建筑有一个房间。因此,基本上,我们是否称其为“属于”或“有”只是视角的不同。
构图是一种强烈的“has-a”关系,因为包含对象拥有它。因此,物体的生命周期是绑定的。这意味着,如果我们销毁所有者对象,其成员也将与之一起被销毁。
例如,在我们之前的例子中,房间与建筑一起被摧毁了。
请注意,这并不意味着包含的对象没有其任何部分就无法存在。例如,我们可以拆除建筑物内的所有墙壁,从而摧毁房间。但这座建筑仍将存在。
就基数而言,包含对象可以有我们想要的任意数量的部分。然而,所有部件都需要恰好有一个容器。
1.1源代码
在Java中,我们可以用一个非静态的内部类来建模:
class Building {
class Room {}
}
或者,我们也可以在方法主体中声明该类。不管是命名类、匿名类还是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();
}
}
请注意,至关重要的是,我们的内部类应该是非静态的,因为它将所有实例绑定到包含类。
通常,包含对象想要访问其成员。因此,我们应该存储他们的参考资料:
class Building {
List<Room> rooms;
class Room {}
}
请注意,所有内部类对象都存储对其包含对象的隐式引用。因此,我们不需要手动存储它来访问它:
class Building {
String address;
class Room {
String getBuildingAddress() {
return Building.this.address;
}
}
}
2.集合
聚合也是一种“有”关系。它与构图的区别在于,它不涉及拥有。因此,对象的生命周期没有绑定:每个对象都可以独立存在。
例如,一辆汽车及其车轮。我们可以取下车轮,它们仍然存在。我们可以安装其他(预先存在的)车轮,或者将这些安装在另一辆车上,一切都会正常运行。
当然,没有轮子或分离轮子的汽车不会像有轮子的汽车那样有用。但这就是为什么这种关系首先存在:将部分组装成一个更大的结构,它能比它的部分有更多的东西。
由于聚合不涉及拥有,因此成员不需要只绑定到一个容器。例如,三角形是由段组成的。但三角形可以共享分段作为其边。
2.1源代码
在Java中,我们可以用一个普通的旧引用来建模聚合:
class Wheel {}
class Car {
List<Wheel> wheels;
}
成员可以是任何类型的类,除了非静态的内部类。
在上面的代码片段中,两个类都有单独的源文件。然而,我们也可以使用静态的内部类:
class Car {
List<Wheel> wheels;
static class Wheel {}
}
请注意,Java只会在非静态内部类中创建隐式引用。正因为如此,我们必须在需要的地方手动保持这种关系:
class Wheel {
Car car;
}
class Car {
List<Wheel> wheels;
}
3.关联
关联是三者之间最薄弱的关系。它不是“有”关系,没有一个对象是另一个对象的一部分或成员。
关联仅意味着对象“认识”对方。例如,一个母亲和她的孩子。
3.1源代码
在Java中,我们可以像聚合一样对关联进行建模:
class Child {}
class Mother {
List<Child> children;
}
我们如何判断引用是聚合还是关联?
嗯,我们不能。区别只是合乎逻辑的:其中一个对象是否是另一个对象的一部分。
此外,我们必须像聚合一样在两端手动维护引用:
class Child {
Mother mother;
}
class Mother {
List<Child> children;
}
4.更复杂的例子
让我们看看一个(有点)更复杂的例子!
我们将模拟一所有院系的大学。教授们在每个系工作,他们之间也有朋友。
我们关闭大学后,这些系会存在吗?当然不存在了,但教授们仍然存在(希望如此)。
我们必须决定哪个更合乎逻辑:我们是否将教授视为系的一部分。或者:他们是否是各部门的成员?
是的,他们是。因此,这是一个聚合体。除此之外,教授可以在多个部门工作。
教授之间的关系是关联的,因为说一个教授是另一个教授的一部分没有任何意义。
Java代码看起来是这样的:
class University {
List<Department> department;
}
class Department {
List<Professor> professors;
}
class Professor {
List<Department> department;
List<Professor> friends;
}
请注意,如果我们依靠 “has-a”、“belongs-to”、“member-of”、“part-of”等术语 ,我们可以更容易地识别对象之间的关系。