Это отличный вопрос. Один я просил в течение многих лет, на конференциях, в видео, в блогах. Я слышал всевозможные ответы. Единственный хороший ответ, который я услышал, это предварительное исполнение:
Различия в производительности в языках. Иногда классы используют преимущества встроенной оптимизации движка, а динамические композиции - нет. В большинстве случаев это гораздо меньше, чем проблемы, связанные с наследованием классов, и обычно вы можете встроить все, что вам нужно для оптимизации производительности, в один класс, обернуть вокруг него заводскую функцию и получить необходимые преимущества без проблемная иерархия классов.
Вы никогда не должны беспокоиться об этом, если не обнаружите проблему. Затем вы должны профилировать и тестировать различия в perf, чтобы сделать обоснованные компромиссы по мере необходимости. Зачастую существуют другие способы оптимизации производительности, которые не включают наследование классов, включая такие приемы, как встраивание, делегирование методов, запоминание чистых функций и т. Д. Perf зависит от конкретного приложения и языкового движка. Профилирование здесь важно.
Кроме того, я слышал много распространенных заблуждений. Наиболее распространенным является путаница с системами типов:
Сопоставление типов с классами (есть несколько существующих ответов, сосредоточенных на этом уже здесь). Композиции могут удовлетворять требованиям полиморфизма путем реализации интерфейсов. Классы и типы являются ортогональными, хотя в большинстве языков, поддерживающих классы, подклассы автоматически реализуют интерфейс суперкласса, поэтому это может показаться удобным.
Есть три очень веских причины, чтобы избежать наследования классов, и рост снова и снова:
Проблема гориллы / банана
"Я думаю, что отсутствие возможности повторного использования возникает в объектно-ориентированных языках, а не в функциональных языках. Поскольку проблема с объектно-ориентированными языками заключается в том, что у них есть вся эта неявная среда, которую они носят с собой. Вы хотели банан, но то, что ты получил, было гориллой, держащей банан и целые джунгли. " ~ Джо Армстронг, цитата из "Кодеров в работе" Питера Сейбела.
Эта проблема в основном связана с отсутствием выборочного повторного использования кода в наследовании классов. Композиция позволяет вам выбирать только те части, которые вам нужны, подходя к дизайну программного обеспечения из подхода «маленьких, многократно используемых деталей», а не создавая монолитные конструкции, которые инкапсулируют все, что связано с определенной функциональностью.
Хрупкая проблема базового класса
Наследование классов - это самая тесная связь, доступная в объектно-ориентированном проектировании, поскольку базовый класс становится частью реализации дочерних классов. Вот почему вы также услышите совет классического «Дизайнерского паттерна» от Gang of Four: «Программируйте интерфейс, а не реализацию».
Проблема с наследованием реализации заключается в том, что даже малейшее изменение внутренних деталей этой реализации может потенциально сломать дочерние классы. Если интерфейс является общедоступным и каким-либо образом доступным для пользователя, это может привести к нарушению кода, о котором вы даже не знаете.
Это причина того, что иерархии классов становятся хрупкими - их трудно изменить по мере их роста с новыми вариантами использования.
Обычный рефрен заключается в том, что мы должны постоянно рефакторинг нашего кода (см. Martin Fowler и др. По экстремальному программированию, гибкому программированию и т. Д.). Ключом к успеху рефакторинга является то, что вы не можете ломать вещи, но, как мы только что видели, трудно реорганизовать иерархию классов, не ломая вещи.
Причина в том, что невозможно создать правильную иерархию классов, не зная всего, что вам нужно знать о сценариях использования, но вы не можете этого знать в развивающемся программном обеспечении. Варианты использования постоянно добавляются или изменяются в проектах.
Существует также процесс обнаружения в программировании, где вы обнаруживаете правильный дизайн при реализации кода и узнаете больше о том, что работает, а что нет.Но с наследованием классов, как только вы начнете таксономию классов, вы загнали себя в угол.
Вам нужно знать информацию, прежде чем начинать реализацию, но часть изучения информации, которая вам нужна, включает построениереализация.Это ловушка-22.
Дублирование по необходимости. Вот где действительно начинается смертельная спираль.Иногда вам действительно нужен банан, а не горилла, держащая банан, и целые джунгли.Таким образом, вы копируете и вставляете это.Теперь есть ошибка в банане, так что вы это исправите.Позже вы получите такой же отчет об ошибке и закроете его.«Я уже исправил это».И тогда вы получите тот же отчет об ошибке снова.И опять.Ой-ой.Это не исправлено.Вы забыли другой банан!Google "скопируйте макароны".
В других случаях вам действительно нужно добавить новый вариант использования в свое программное обеспечение, но вы не можете изменить исходный базовый класс, поэтому вместо этого вы копируете и вставляете весь классиерархия в новую и переименуйте все классы, которые вам нужны в иерархии, чтобы включить этот новый вариант использования в базу кодаСпустя 6 месяцев новый разработчик просматривает код и задается вопросом, от какой иерархии классов унаследовать, и никто не может дать хороший ответ.
Дублирование по необходимости приводит к копированию беспорядка пасты, и довольно скоро люди начинают разбрасываться вокругСлово «переписать», как будто это не имеет большого значения.Проблема заключается в том, что большинство проектов переписать не удастся.Я могу назвать несколько организаций, которые в настоящее время поддерживают две команды разработчиков вместо одной, пока они работают над проектом переписывания.Я видел, как такие организации урезали финансирование одному или другому, и я видел, как такие проекты жуют столько денег, что у стартапа или малого бизнеса заканчиваются деньги и он закрывается.
Разработчики недооцениваютвлияние наследования классов все время.Это важный выбор, и вам нужно знать о компромиссах, которые вы выбираете при каждом создании или наследовании базового класса.