The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:
Мы все знаем, что наследование плохо, и что композиция вместо наследования - это хорошая идея, но действительно ли мы понимаем, почему? Во всех статьях на эту тему, которые я нашел, авторы утверждают, что наследование может быть вредным для вашего кода, поэтому лучше не использовать его. Меня смущает эта часть “лучше”; это значит, что иногда наследование имеет смысл? Несколько недель назад я провел интервью с Дэвидом Уэстом (автором моей любимой книги об ООП Object Thinking), и он сказал, что наследование в объектно-ориентированном программировании не должно существовать (полное видео). Возможно, доктор Уэст прав и мы должны полностью забыть ключевое слово extends
в Java, например?
Я считаю, что мы должны. И я знаю, почему.
Это не потому, что мы вводим ненужную связность, как сказал Аллен Холуб в своей статье Почему extends плох. Он безусловно был прав, но я считаю, что это не корень проблемы.
“Наследовать”, как английский глагол, имеет несколько значений. Это - то, что, по-моему, имели в виду создатели наследования в Simula: “Получать (качество, характеристику или предрасположенность) генетически от своих родителей или предков.”
Перенять характеристику от другого объекта - это замечательная идея, и это называется подтипизация. Она прекрасно вписывается в ООП и на самом деле позволяет реализовать полиморфизм: Объект класса Article
наследует все характеристики объектов класса Manuscript
и добавляет свои собственные. Например, он наследует способность печатать себя и добавляет способность представить себя на конференцию.
Это подтипизация, и это идеальная техника; когда требуется рукопись, мы можем предоставить статью, и никто не заметит ничего, потому что тип Article
является подтипом типа Manuscript
(принцип подстановки Лисков).
Но что общего у копирования методов и атрибутов из родительского класса в дочерний с “получением характеристик”? Наследование реализации именно это - копирование - и оно никак не связано с значением слова “наследовать”, которое я привел выше.
Наследование реализации гораздо ближе к другому значению: “Получать (деньги, собственность или титул) как наследник после смерти предыдущего обладателя.” Кто мертв, спросите вы? Объект считается мертвым, если он позволяет другим объектам наследовать его инкапсулированный код и данные. Это наследование реализации:
Класс Article
копирует метод print()
и атрибут body
из класса Manuscript
, как будто это не живой организм, а скорее мертвый, откуда мы можем наследовать его части, “деньги, имущество или звание”.
Наследование реализации было создано как механизм для повторного использования кода и оно совсем не подходит для ООП. Да, вначале это может показаться удобным, но это абсолютно неправильно с точки зрения объектного мышления. Как и геттеры и сеттеры, наследование реализации превращает объекты в контейнеры с данными и процедурами. Конечно, удобно скопировать некоторые из этих данных и процедур в новый объект, чтобы избежать дублирования кода. Но в этом нет смысла объектов. Они не мертвы; они живы!
Не убивайте их наследованием!
Таким образом, я считаю наследование плохим, потому что это процедурная техника для повторного использования кода. Неудивительно, что оно вызывает проблемы, о которых люди говорят уже много лет. Потому что это процедурное наследование! Вот почему оно не подходит для объектно-ориентированного программирования.
Кстати, мы обсуждали эту проблему в нашем чате Gitter (он уже мертв) неделю назад, и тогда мне стало ясно, в чем именно заключается проблема наследования. Взгляните на наше обсуждение там.
Translated by ChatGPT gpt-3.5-turbo/42 on 2023-12-17 at 16:17