Всегда ли наследование должно идти от родителя к потомкам?
Возможно, этот вопрос уже давно обсосан кучей способов, но я дошел до него только сейчас: всегда ли наследование должно идти от родителя к потомкам?
Стандартно во всяких учебниках для начинающих рассказывают, что наследование является аналогом связи «Является». Например, яблоко является фруктом, поэтому в коде класс Яблока должен наследоваться от класса Фрукт.
Что еще нужно учитывать, чтобы усомниться в утвердительном ответе на вопрос в заголовке?
Наследник может изменять методы родителя
Наследник может хранить больше полей, чем родитель
Наследник не может удалять поля родителя
Что получается тогда? Возьмем пример с геометрическими фигурами. Есть у нас прямоугольник, площадь которого вычисляется по формуле
. Получается, что в прямоугольнике нам нужно два поля — стороны
. Но есть квадрат, который является прямоугольником, поэтому и класс Квадрат должен наследоваться от класса Прямоугольник. Проблемы, с учетом правил выше, возникают уже на этом этапе: если формула площади квадрата
то зачем нам хранить дополнительно сторону
, которая равна стороне
? Получается, что мы впустую тратим память.
Если пойти еще выше по родителям, то прямоугольник является параллелограммом. Площадь параллелограмма вычисляется по формуле
, где Q - угол между сторонами. Получается, что в прямоугольнике и, следовательно, в квадрате нам тоже нужно хранить этот угол, а использовать его мы никак не будем. Снова тратим память впустую.
Другим видом параллелограмма является ромб (
), в котором мы снова бесполезно храним размерность второй стороны.
И если так подумать, то параллелограмм является выпуклым прямоугольником, который вписывается в окружность. В общем случае
, где
. Получается, что в параллелограмме нужно хранить не только две стороны и угол, которые затем тянутся выше, но и еще две стороны, которые также тянутся выше. Вот и получается, что в квадрате у нас хранятся отдельно все четыре стороны и угол между двумя из них.
Рассматривая наследование как метод расширения функционала, здесь гораздо «правильнее» в качестве родителя выбрать квадрат. Он хранит всего лишь одну сторону.
Далее от него потомки идут в две стороны.
Сторона первая: квадрат -> ромб (добавляем угол) -> параллелограмм (добавляем вторую сторону)
Сторона вторая: квадрат -> прямоугольник (добавляем вторую сторону) -> выпуклый четырехугольник (добавляем еще две стороны)
Как будто, это выглядит более логично? Или я где-то ошибся? Очень жду профессионального мнения в комментариях