The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:
我们都知道继承是不好的,并且组合优于继承是一个好主意,但我们真的理解为什么吗?在我找到的(继承是不好的)所有文章中,作者都说继承可能对你的代码有害,所以最好不要使用它。这个“最好”的部分让我困扰;这是否意味着有时候继承是有意义的?我几周前采访了David West(我最喜欢的关于面向对象编程的书《Object Thinking》的作者),他说继承在面向对象编程中根本不应该存在(完整视频)。也许韦斯特博士是对的,我们应该完全忘记Java中的extends
关键字,例如?
我认为我们应该这么做。而且我认为我知道原因是什么。
并不是因为像Allen Holub在他的为什么extends是邪恶的文章中所说的那样引入了不必要的耦合。他肯定是对的,但我相信这不是问题的根本原因。
作为一个英语动词,“inherit”有许多含义。我猜继承发明者在Simula中理解的是这个:从父母或祖先那里遗传(一种品质、特征或倾向)。
从另一个对象派生一个特征是一个很好的主意,它被称为子类型。它完全适合面向对象编程,实际上还实现了多态性:一个Article
类的对象继承了Manuscript
类对象的所有特征,并添加了自己的特征。例如,它继承了自我打印的能力,并添加了将自己提交到会议的能力:
interface Manuscript {
void print(Console console);
}
interface Article extends Manuscript {
void submit(Conference cnf);
}
这就是子类型化,它是一种完美的技术;每当需要一份手稿时,我们可以提供一篇文章,而没有人会注意到什么,因为类型Article
是类型Manuscript
的子类型(里斯科夫替换原则)。
但是,将父类的方法和属性复制到子类中,与”派生特征”有什么关系呢?实现继承正是这样——复制——它与我上面引用的”继承”这个词的意义无关。
实现继承更接近于一个不同的意义:”在前任持有人去世时作为继承人接收(金钱、财产或头衔)”。你会问谁去世了?如果一个对象允许其他对象继承它封装的代码和数据,那么它就是”死亡”的。这就是实现继承:
class Manuscript {
protected String body;
void print(Console console) {
console.println(this.body);
}
}
class Article extends Manuscript {
void submit(Conference cnf) {
cnf.send(this.body);
}
}
类Article
从类Manuscript
复制了方法print()
和属性body
,就好像它不是一个有生命的有机体,而是一个我们可以继承其部分的死有机体,”金钱、财产或头衔”。
实现继承是为了代码复用而创建的机制,在面向对象编程中根本不适用。是的,一开始看起来可能很方便,但从面向对象的角度来看是完全错误的。就像获取器和设置器一样,实现继承将对象转变为带有数据和过程的容器。当然,将其中一些数据和过程复制到新对象中以避免代码重复是方便的。但这不是对象的本质。它们不是死的;它们是活的!
不要用继承来杀死它们!
因此,我认为继承是不好的,因为它是一种用于代码复用的过程化技术。毫不奇怪,它引入了多年来人们一直在讨论的所有问题。因为它是过程化的!这就是为什么它不适用于面向对象编程的原因。
顺便说一下,我们在我们的Gitter聊天室(已经死了)一周前讨论过这个问题,那时我才明白继承到底有哪些问题。请看看我们在那里的讨论。
Translated by ChatGPT gpt-3.5-turbo/36 on 2023-10-30 at 04:45