对于某些情况,推荐使用组合(composition)而不是继承(inheritance)。我看到这在 Ruby 和 Javascript 社区中越来越普遍。
组合听起来很像多重继承。我甚至读到过,在一些 Ruby 实现内部,模块组合实际上就是带有轻微语法糖的多重继承。
它是相同的吗?如果不是,那么它与多重继承有什么不同?
组合听起来很像多重继承。我甚至读到过,在一些 Ruby 实现内部,模块组合实际上就是带有轻微语法糖的多重继承。
它是相同的吗?如果不是,那么它与多重继承有什么不同?
这取决于您所指的“多继承”和“组合”的含义。如果组合和继承都只是向对象响应的消息列表中添加内容,那么它们在定义上是相等的。
假设类只是方法的虚拟表,每个语言中的对象都由对类的引用和一些数据定义。如果一个对象通过调用与其类关联的方法查找函数来响应消息,而该方法查找函数返回该方法(如果类包含与消息对应的方法)或递归调用其超类的方法查找函数,则我们拥有了带有单继承和无组合的语言。可以通过按照Open Extensible Object Models第2.2节所述修改方法查找函数实现在此语言中添加多重继承,由Ian Piumarta编写。基本上就是添加一个将方法查找延迟到多个其他类而不仅仅是一个类的类。可以很容易地看出,混入/特征(我假设这就是你所说的组合)可以以完全相同的方式添加。
然而,如果你所说的“组合”是指一个对象具有其他对象作为实例变量,那么使用它而不是多重继承是有很好的理由的:封装。如果可以在父对象上调用实例变量中的对象的某些方法,则这些方法在实例变量的对象上可能就没有意义了。很多人以技术方式谈论多重继承,但我想谈论设计方面。
在良好的面向对象编程(OOP)设计中,不应该使用多重继承。为什么?
1.
像GRASP patterns所建议的高内聚模式一样,一个类不应该有太多的责任。
2.
继承锁定了你的架构。如果你想要改变任何东西,你必须改变所有东西。我给你一个例子:想象一些类如car
,truck
,boat
和plane
继承自类motorVehicle
。现在,我添加一个新概念:汽车、卡车、船和飞机也是运动车辆。我该怎么办?
motorVehicle
而不是movingVehicle
呢?motorVehicle
和movingVehicle
。我的类将有两个不同但高度相关的职责,这真的很糟糕。如果我添加一辆自行车会怎么样!这就是为什么继承应该只用于为具有相同责任的类因式分解代码!
结论
如果一个类应该只有一个明确定义的职责,并且您应该仅将继承用于具有相同职责的类,则在正确的OOP设计中永远不应使用多重继承。
我建议您始终使用接口的组合来使您的类之间的耦合度低。这将为您提供更可扩展,可维护和可测试的代码!
在前面的例子中,使用组合将导致一些电机类继承自电机接口,一些移动类继承自移动接口,而类“car”、“truck”等将“使用”电机接口和移动接口。而我们的“bicycle”只能使用移动接口!class Fruit {
...
}
class Apple extends Fruit {
...
}
组成
class Fruit {
...
}
class Apple {
this.fruit = new Fruit();
}
child
是一个parent
,那么使用继承;否则使用组合。在上面的示例中,我们可以说Apple
是一个Fruit
,因此在这里使用继承可能是有意义的。再次强调,这只是一个建议,而不是规则!
请查看这篇文章,以获得更深入的解释。