组合(Composition) 和 聚合(Aggregation) 是面向对象编程中的两种常见关系,它们都表示“整体-部分”或“拥有”的关系,但在语义和实现方式上有所不同。理解这两者有助于更好地设计类之间的关系和结构。
1. 组合(Composition)
基本概念
- 组合表示一种强的“整体-部分”关系,通常也称为“has-a”关系。一个类作为整体(Whole),由多个部分(Part)构成。
- 组合关系中的部分对象完全依赖于整体对象的存在。如果整体对象被销毁,部分对象也会随之销毁。
实现方式
- 在组合中,部分对象通常作为整体对象的成员变量被包含在类中。部分对象的生命周期由整体对象管理,通常是在整体对象创建时初始化,并在整体对象销毁时销毁。
示例
#include <iostream>
class Engine {
public:
Engine() {
std::cout << "Engine created" << std::endl;
}
~Engine() {
std::cout << "Engine destroyed" << std::endl;
}
};
class Car {
private:
Engine engine; // Engine 是 Car 的一部分
public:
Car() {
std::cout << "Car created" << std::endl;
}
~Car() {
std::cout << "Car destroyed" << std::endl;
}
};
int main() {
Car myCar; // 当 Car 对象创建时,Engine 对象也会创建
return 0; // 当 Car 对象销毁时,Engine 对象也会销毁
}
解释:
- 在这个例子中,
Car
类中包含了一个Engine
对象,表示组合关系。 - 当
Car
对象创建时,Engine
对象也随之创建;当Car
对象销毁时,Engine
对象也随之销毁。这表明Engine
对象的生命周期完全依赖于Car
对象。
2. 聚合(Aggregation)
基本概念
- 聚合也表示“整体-部分”关系,但它是一种较弱的关系。部分对象可以独立存在于整体对象之外,整体对象与部分对象之间存在“has-a”关系,但不是“拥有”关系。
- 在聚合关系中,部分对象的生命周期独立于整体对象。即使整体对象销毁,部分对象仍然可以继续存在。
实现方式
- 在聚合中,部分对象通常作为整体对象的成员变量,但它们是通过外部传递进来的(例如通过构造函数、setter方法等),而非由整体对象直接创建和销毁。
示例
#include <iostream>
class Engine {
public:
Engine() {
std::cout << "Engine created" << std::endl;
}
~Engine() {
std::cout << "Engine destroyed" << std::endl;
}
};
class Car {
private:
Engine* engine; // Engine 通过指针被 Car 引用,表示聚合关系
public:
Car(Engine* eng) : engine(eng) {
std::cout << "Car created" << std::endl;
}
~Car() {
std::cout << "Car destroyed" << std::endl;
}
};
int main() {
Engine* myEngine = new Engine(); // Engine 独立创建
Car myCar(myEngine); // Car 使用外部的 Engine 对象
delete myEngine; // 手动销毁 Engine 对象
return 0;
}
解释:
- 在这个例子中,
Engine
对象是独立创建的,Car
类通过指针引用这个Engine
对象,表示聚合关系。 Engine
对象的生命周期不依赖于Car
对象,哪怕Car
对象销毁了,Engine
对象仍然可以存在或被其他对象使用。
3. 区别总结
-
生命周期依赖性:
- 组合:部分对象的生命周期完全依赖于整体对象,整体对象销毁时,部分对象也会销毁。
- 聚合:部分对象的生命周期独立于整体对象,部分对象可以在整体对象销毁后继续存在。
-
所有权:
- 组合:整体对象“拥有”部分对象,整体对象负责创建和销毁部分对象。
- 聚合:整体对象与部分对象之间没有强的所有权关系,部分对象通常是外部传递进来的,并且外部可能仍然持有对部分对象的控制权。
-
实现方式:
- 组合:部分对象作为类的成员变量直接包含在整体对象中。
- 聚合:部分对象通过指针或引用传递到整体对象中,并在整体对象中存储其引用。
4. 何时使用组合与聚合?
-
使用组合:
- 当部分对象不能独立存在,且其生命周期应该完全依赖于整体对象时,例如:
Person
类和Heart
类,Heart
是Person
的一部分,不能独立存在。
- 当部分对象不能独立存在,且其生命周期应该完全依赖于整体对象时,例如:
-
使用聚合:
- 当部分对象可以独立存在,并且可能会被多个整体对象共享时,例如:
Library
类和Book
类,Book
可以独立存在,并且可以属于不同的Library
对象。
- 当部分对象可以独立存在,并且可能会被多个整体对象共享时,例如:
5. 总结
组合和聚合都是表示类之间“整体-部分”关系的概念。组合强调部分对象依赖于整体对象,而聚合则允许部分对象独立于整体对象存在。在设计类之间的关系时,根据需求选择使用组合或聚合,可以帮助构建更清晰、逻辑更合理的系统。