定义
组合/聚合复用原则(Composite/Aggregate Reuse Principle, CARP),又称为合成/聚合复用原则,提倡在一个新的对象里使用一些已有的对象的功能,使得部分功能在多个类之间共享,而非通过继承获得。这是通过一个对象的属性引用其他对象来实现的。
应用场景
CARP 主要适用于需要重用现有功能,但又想要避免通过继承引入强耦合和脆弱的基类问题的场景。常见的应用场景包括:
- 当需要组合多个对象的行为以创建一个复杂对象时。
- 当需要共享资源或行为,但不需要继承关系的完整语义时。
示例与反例
示例:
// 组合示例
class Engine {
void start() {
// 启动发动机的逻辑
}
}
class Car {
private Engine engine;
Car(Engine engine) {
this.engine = engine;
}
void startCar() {
engine.start();
}
}
在这个例子中,Car
类不是从 Engine
类继承,而是包含一个 Engine
对象,它利用了 Engine
的 start
方法。
反例:
// 继承反例
class Engine {
void start() {
// 启动发动机的逻辑
}
}
// Car 类通过继承 Engine 类来复用 start 方法
class Car extends Engine {
void startCar() {
start();
}
}
在这个反例中,Car
类通过继承 Engine
类来复用 start
方法。这样做创建了一个“汽车是一个发动机”的不合理关系,并引入了不必要的耦合。
原则间的权衡与冲突
- 与开闭原则(OCP):组合支持 OCP,因为它允许系统更容易地扩展新的行为,而不需要修改现有代码。
- 与单一职责原则(SRP):通过组合,可以更好地遵循 SRP,因为它鼓励将功能分解为独立的类。
- 与里氏替换原则(LSP):组合不直接涉及继承和LSP,但使用组合可以避免错误的继承结构,从而间接支持 LSP。
设计原则的局限性
- 过度使用组合可能会导致系统中出现过多的间接层次,增加了系统的复杂性。
- 在某些情况下,组合可能无法充分利用现有类层次结构中的代码,因为组合不会暴露继承层次结构中的受保护成员。
总结与建议
- 优先使用组合:在设计类时,首先考虑是否可以通过组合其他类的方式来复用代码,而不是通过继承。
- 适当使用继承:继承应该在明确有一个“是一个”关系时使用,并且当你想要使用多态性时。
- 清晰界定职责:确保你的组件有清晰和单一的职责,这样它们就可以被轻松地组合。
- 理解关系:清楚地理解组合和聚合之间的区别,聚合意味着整体和部分之间有一个弱关联,而组合意味着整体和部分之间是强关联。
- 设计灵活性:组合提供了更高的设计灵活性,因为它可以在运行时动态改变对象的行为。