为什么更喜欢组合而不是继承?每种方法都有哪些权衡?什么时候应该选择继承而不是组合?
当前回答
在可以枚举不变量的情况下,子类型更合适,功能更强大,否则使用函数组合实现可扩展性。
其他回答
继承是非常强大的,但你不能强迫它(参见:圆-椭圆问题)。如果你真的不能完全确定一个真正的“is-a”子类型关系,那么最好是组合。
当你在两个类之间有一个is-a关系(例如狗是狗),你就去继承。
另一方面,当你在两个班级(学生有课程)或(老师学习课程)之间有一种或某种形容词关系时,你选择了作文。
你什么时候可以用作文?
你可以一直使用合成。在某些情况下,继承也是可能的,并可能导致更强大和/或更直观的API,但组合始终是一种选择。
什么时候可以使用继承?
人们常说,如果“bar是foo”,那么bar类可以继承foo类。不幸的是,仅此测试并不可靠,请改用以下方法:
bar是foo,AND酒吧可以做foos所能做的一切。
第一个测试确保Foo的所有getter在Bar中都有意义(=共享的财产),而第二个测试确保所有Foo的setter都在Bar(=共享功能)中有意义。
示例:狗/动物
狗是一种动物,狗可以做动物所能做的一切(如呼吸、移动等)。因此,狗类可以继承动物类。
反例:圆/椭圆
圆是椭圆,但圆不能做椭圆所能做的一切。例如,圆不能拉伸,而椭圆可以拉伸。因此,Circle类不能继承Ellipse类。
这被称为圆椭圆问题,这并不是一个真正的问题,但更多的是表明“一个条就是一个foo”本身不是一个可靠的测试。特别是,这个例子强调了派生类应该扩展基类的功能,而不是限制它。否则,基类不能以多态的方式使用。添加测试“bar可以做foos所能做的一切”确保了多态性的使用是可能的,这相当于Liskov替换原则:
使用基类指针或引用的函数必须能够在不知道的情况下使用派生类的对象
什么时候应该使用继承?
即使你可以使用继承,也不意味着你应该这样做:使用组合总是一种选择。继承是一个强大的工具,允许隐式代码重用和动态分派,但它确实有一些缺点,这就是为什么组合经常被首选的原因。继承和组合之间的权衡并不明显,在我看来,在lcn的回答中可以得到最好的解释。
作为经验法则,当多态性使用被认为非常普遍时,我倾向于选择继承而不是组合,在这种情况下,动态分派的功能可以导致更可读和更优雅的API。例如,在GUI框架中有一个多态类Widget,或者在XML库中有一种多态类Node,这样就可以使用一个比纯基于组合的解决方案更易读、更直观的API。
这条规则完全是无稽之谈。为什么?
原因是,在任何情况下,都可以区分是使用组合还是继承。这取决于对一个问题的回答:“是什么?是什么?”或“有什么?”其他的东西“。
你不能“宁愿”把某样东西变成另一样东西或拥有另一样。严格的逻辑规则适用。
也没有“人为的例子”,因为在任何情况下都可以给出这个问题的答案。
如果你不能回答这个问题,还有其他问题。这包括类的责任重叠,这通常是错误使用接口的结果,较少是通过在不同类中重写相同的代码。
为了避免这种情况,我还建议为类使用与它们的职责完全相似的好名称。
合成与继承是一个广泛的主题。对于什么更好,没有真正的答案,因为我认为这一切都取决于系统的设计。
通常,对象之间的关系类型为选择其中一个对象提供了更好的信息。
如果关系类型是“is-A”关系,则继承是更好的方法。否则关系类型为“HAS-A”关系,则组合将更接近。
这完全取决于实体关系。