为什么更喜欢组合而不是继承?每种方法都有哪些权衡?什么时候应该选择继承而不是组合?


当前回答

你想强迫自己(或其他程序员)坚持什么,什么时候你想让自己(或另一个程序员)有更多的自由。有人认为,当你想强迫某人以某种方式处理/解决某个特定问题时,继承是有帮助的,这样他们就不会走错方向。

Is-a和Has-a是一个有用的经验法则。

其他回答

在Java或C#中,对象一旦实例化就不能更改其类型。

因此,如果您的对象需要显示为不同的对象或根据对象的状态或条件表现出不同的行为,那么请使用组合:参考状态和策略设计模式。

如果对象需要具有相同的类型,则使用继承或实现接口。

我看没有人提到钻石问题,这可能是继承带来的。

总之,如果类B和C继承了a,并且都重写了方法X,而第四个类D继承了B和C,并且没有重写X,那么应该使用X D的哪个实现?

维基百科对这个问题中讨论的主题提供了一个很好的概述。

在可以枚举不变量的情况下,子类型更合适,功能更强大,否则使用函数组合实现可扩展性。

正如许多人所说的,我首先要检查是否存在“是”的关系。如果存在,我通常检查以下内容:

是否可以实例化基类。也就是说,基类是否可以是非抽象的。如果可以是非抽象的,我通常更喜欢写作

例如1。会计是员工。但我不会使用继承,因为Employee对象可以实例化。

例如2。书籍是SellingItem。SellingItem无法实例化-它是抽象概念。因此,我将使用遗传痤疮。SellingItem是一个抽象基类(或C#中的接口)

你觉得这种方法怎么样?

此外,我支持为什么要使用继承?

使用继承的主要原因不是作为组合的一种形式,而是为了让您可以获得多态行为。如果不需要多态性,那么可能不应该使用继承。

@马修。在https://softwareengineering.stackexchange.com/questions/12439/code-smell-inheritance-abuse/12448#comment303759_12448

继承的问题在于它可以用于两个正交的目的:接口(用于多态性)实现(用于代码重用)

参考

哪个班级设计更好?继承与聚合

你什么时候可以用作文?

你可以一直使用合成。在某些情况下,继承也是可能的,并可能导致更强大和/或更直观的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。