Answers:
戈斯林没有说要避免使用extends关键字……他只是说过不要将继承作为实现代码重用的一种手段。
继承本身并不坏,它是创建OO结构时使用的非常强大的(必要的)工具。但是,如果使用不当(即用于创建对象结构以外的其他用途),则会创建紧密耦合且难以维护的代码。
因为应该使用继承来创建多态结构,所以可以通过接口轻松地完成继承,因此在设计对象结构时要声明使用接口而不是类。如果您发现自己使用扩展作为避免将代码从一个对象复制粘贴到另一个对象的一种方式,则可能是您在使用它时错了,最好与专门的类一起使用,以处理此功能并通过合成来共享该代码。
阅读并了解Liskov替代原理。每当您要扩展类(或与此相关的接口)时,请问自己是否违反了该原理,如果是,则您很有可能(滥用)以不自然的方式使用继承。它很容易做到,而且似乎通常是对的,但这会使您的代码难以维护,测试和发展。
---编辑 此答案是一个很好的论据,可以更笼统地回答该问题。
因为您只能扩展一个类,但可以实现许多接口。
我曾经听说继承定义了您的身份,但是接口定义了您可以扮演的角色。
接口还可以更好地利用多态性。
那可能是原因的一部分。
我也被教过这一点,并且在可能的地方我更喜欢接口(当然,在有意义的地方我仍然使用继承)。
我认为它做的一件事是将您的代码与特定的实现分离。假设我有一个名为ConsoleWriter的类,该类被传递到方法中以将某些内容写出并将其写入控制台。现在让我说我想切换到打印到GUI窗口。好了,现在我必须修改方法或编写一个以GUIWriter作为参数的新方法。如果我先定义一个IWriter接口,然后将该方法放入IWriter中,那么我可以从ConsoleWriter(它将实现IWriter接口)开始,然后再编写一个新的类GUIWriter(它也实现IWriter接口),然后我只需要切换正在传递的课程即可。
另一件事(对于C#是正确的,对于Java不确定)是您只能扩展1个类,但可以实现许多接口。可以说我有一个名为Teacher,MathTeacher和HistoryTeacher的类。现在,MathTeacher和HistoryTeacher从Teacher继承而来,但是如果我们要一个代表既是MathTeacher又是HistoryTeacher的人的类,该怎么办?当您一次只能从一个类继承多个类时,它可能会变得非常混乱(有办法,但它们并不是最佳选择)。使用接口,您可以有2个称为IMathTeacher和IHistoryTeacher的接口,然后有一个从Teacher扩展并实现这2个接口的类。
使用接口的一个缺点是,有时我会看到人们重复代码(因为您必须为每个类创建实现,因此必须实现接口),但是对于此问题有一种干净的解决方案,例如使用诸如委托之类的东西(不确定等价于Java)。
人们认为在继承上使用接口的最大原因是实现代码的解耦,但不要认为继承是有害的,因为它仍然非常有用。
如果从一个类继承,则不仅要依赖该类,而且还必须遵守该类的客户创建的任何隐式合同。鲍勃叔叔提供了一个很好的例子,说明如何使用基本的Square扩展Rectangle难题轻松地违反Liskov换位原理。接口由于没有实现,因此也没有隐藏的合同。