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


当前回答

我听过的一条经验法则是,当它是一种“is-A”关系时,应该使用继承;当它是“has-A”关系的时候,应该使用组合。尽管如此,我觉得你应该始终倾向于构图,因为它消除了很多复杂性。

其他回答

继承带来了所有不可否认的好处,下面是它的一些缺点。

继承的缺点:

您不能在运行时更改从超级类继承的实现(显然是因为继承是在编译时定义的)。继承将子类暴露给其父类实现的细节,这就是为什么人们常说继承破坏了封装(从某种意义上讲,您真正需要关注的是接口而不是实现,所以通过子类重用并不总是首选的)。继承提供的紧密耦合使得子类的实现与超级类的实现紧密结合,父类实现中的任何更改都将迫使子类进行更改。子类的过度重用会使继承堆栈变得很深,也很混乱。

另一方面,通过对象获取对其他对象的引用,在运行时定义对象组合。在这种情况下,这些对象将永远无法访问彼此的受保护数据(没有封装中断),并将被迫尊重彼此的接口。在这种情况下,实现依赖性也会比继承的情况下少得多。

你需要看看Bob叔叔的SOLID类设计原则中的Liskov替换原则。:)

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

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

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

当你在两个类之间有一个is-a关系(例如狗是狗),你就去继承。

另一方面,当你在两个班级(学生有课程)或(老师学习课程)之间有一种或某种形容词关系时,你选择了作文。

要从新程序员的不同角度解决这个问题:

当我们学习面向对象的编程时,继承经常很早就被教授,因此它被视为一个常见问题的简单解决方案。

我有三个类都需要一些通用功能。所以如果我编写一个基类并让它们全部继承,然后它们将所有这些都有这个功能,我只需要维护一次位置

这听起来很好,但实际上它几乎从未奏效,原因如下:

我们发现,我们希望我们的类具有其他一些功能。如果我们向类添加功能的方式是通过继承,那么我们必须决定——我们是否将其添加到现有基类中,即使不是每个从其继承的类都需要该功能?我们是否创建另一个基类?但是,已经从其他基类继承的类呢?我们发现,对于从基类继承的一个类,我们希望基类的行为稍有不同。所以现在我们回过头来修改我们的基类,可能会添加一些虚拟方法,或者更糟的是,一些代码会说,“如果我是继承的类型A,那么就这样做,但如果我是传承的类型B,那么就那样做。”这是很糟糕的,原因很多。一个是,每次我们改变基类时,我们都在有效地改变每个继承的类。所以我们真的在改变A、B、C和D类,因为我们需要在A类中有一个稍微不同的行为。尽管我们认为我们很小心,但我们可能会因为与这些类无关的原因而破坏其中一个类。我们可能知道为什么我们决定让所有这些类彼此继承,但这对其他必须维护我们代码的人来说可能没有意义。我们可能会迫使他们做出一个艰难的选择——我是做一些非常丑陋和混乱的事情来做出我需要的改变(见前面的要点),还是只是重写一堆内容。

最后,我们把代码绑在一些困难的地方,除了说:“酷,我了解了继承,现在我使用了它。”这并不意味着居高临下,因为我们都这样做了。但我们都这么做了,因为没有人告诉我们不要这样做。

当有人向我解释“喜欢组合而不是继承”时,我回想了一下每次尝试使用继承在类之间共享功能时,我都意识到大多数时候它并没有真正发挥作用。

解药是单一责任原则。将其视为约束。我的班级必须做一件事。我必须能够给我的班级起一个名字,以某种方式描述它所做的一件事。(凡事都有例外,但当我们学习时,绝对规则有时会更好。)因此,我无法编写名为ObjectBaseThatContainsVariousFunctionsNeedByDifferentClasses的基类。我所需要的任何不同的功能都必须在它自己的类中,然后需要该功能的其他类可以依赖于该类,而不是从该类继承。

在过于简化的风险下,这就是组合——组合多个类一起工作。一旦我们养成了这个习惯,我们就会发现它比使用继承更灵活、更可维护、更可测试。