1. 引言
在现实世界和编程中,对象之间存在各种关系。理解这些关系有时候并不容易,尤其是组合(Composition)、聚合(Aggregation)和关联(Association)这三者之间的区别。
本文将深入讲解这三种常见的对象关系,帮助你准确识别并正确使用它们。
2. 组合(Composition)
组合是一种“属于”关系,也常被称为“has-a”关系(与“is-a”关系即继承相对)。
组合是一种强“has-a”关系。一个对象拥有另一个对象,生命周期绑定在一起。如果拥有者被销毁,其组成部分也将被销毁。
例如,一个房间属于一栋建筑。如果建筑被拆除,房间自然也不复存在。
2.1 UML 表示
在 UML 中,组合用实心菱形箭头表示:
更清晰的带箭头版本:
Building-Room 示例:
2.2 示例代码
在 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;
}
}
}
3. 聚合(Aggregation)
聚合也是一种“has-a”关系,但与组合不同,它不涉及拥有权。对象之间生命周期独立。
例如,汽车和轮胎的关系。轮胎可以被拆下,安装到其他车上,仍然有效。
3.1 UML 表示
聚合用空心菱形表示:
汽车与轮胎示例:
3.2 示例代码
聚合通常通过普通引用实现:
class Wheel {}
class Car {
List<Wheel> wheels;
}
也可以使用静态内部类:
class Car {
List<Wheel> wheels;
static class Wheel {}
}
如果需要双向引用,需手动维护:
class Wheel {
Car car;
}
class Car {
List<Wheel> wheels;
}
4. 关联(Association)
关联是三者中最弱的关系。它不是“has-a”关系,而是对象之间相互“知道”对方的存在。
例如,母亲和孩子之间的关系。
4.1 UML 表示
单向关联用箭头表示:
双向关联可以用双箭头或无箭头线:
母亲与孩子示例:
4.2 示例代码
关联与聚合类似,通过引用实现:
class Child {}
class Mother {
List<Child> children;
}
如果是双向关联,需要手动维护双方引用:
class Child {
Mother mother;
}
class Mother {
List<Child> children;
}
5. UML 补充说明
有时我们会在 UML 图中表示关系的“基数”(cardinality)来增强可读性:
0 通常不用于表示关系,除非用范围表示可选关系:
组合关系中因为只有一个拥有者,所以通常不标注。
6. 综合示例
假设我们要建模一所大学,包含院系、教授,教授之间还有朋友关系。
- 大学关闭后,院系不存在 → 组合
- 教授离开大学后仍存在,且可能属于多个院系 → 聚合
- 教授之间是朋友 → 关联
对应的 UML 图:
Java 实现如下:
class University {
List<Department> departments;
}
class Department {
List<Professor> professors;
}
class Professor {
List<Department> departments;
List<Professor> friends;
}
✅ 小技巧: 使用“has-a”、“belongs-to”、“member-of”等术语有助于快速识别对象之间的关系。
7. 总结
组合、聚合和关联是面向对象设计中三种基本的关系:
类型 | 生命周期绑定 | 拥有关系 | UML 表示 |
---|---|---|---|
组合 | ✅ | ✅ | 实心菱形 |
聚合 | ❌ | ❌ | 空心菱形 |
关联 | ❌ | ❌ | 箭头或无箭头线 |
理解这些关系有助于我们设计出结构清晰、易于维护的系统。在实际编码中,注意使用合适的类结构和引用方式来准确表达对象之间的关系。
完整示例代码可在 GitHub 获取。