Зарегистрируйтесь, чтобы продолжить обучение

Композиция вместо наследования JS: Погружаясь в классы

Наследование — один из самых противоречивых механизмов ООП. Чем больше мы узнаем о нем, тем больше подводных камней встречается. Мало того, что оно добавляет в код невероятное количество новых понятий и особенностей поведения, так оно еще имеет фундаментальные изъяны. И если с первым все более менее понятно — на протяжении всех предыдущих уроков мы только и занимались тем, что переосмысливали работу с классами, то со вторым нужно разобраться.

Ключевая проблема с иерархиями в том, что наш мир не иерархичен. Любая классификация всегда опирается на конкретный признак, который интересует нас в конкретный момент времени. И эта же классификация становится бесполезной, если берется другой признак. Это хорошо видно в интернет-магазинах, у которых навороченные фильтры для выбора товара: группировка по производителю, по применимости, по безопасности для детей и так далее. В каждой конкретной ситуации будет своя структура.

Возьмем понятие User. Статьи по наследованию часто любят показывать иерархии пользователей создавая у разработчиков уверенность, что мир так и устроен. Давайте попробуем прикинуть, по каким признакам можно построить иерархию пользователей:

  • По полу (MaleUser, FemaleUser)
  • На основе аутентификации (User, Guest)
  • По роли (Admin, Member)
  • По типу должности (Marketer, SalesManager, Programmer, Tester, Player)
  • По принадлежности к какой-либо группе (UserFromRussia, UserWhoLikesSpartak)
  • По источнику (UserFromFacebook, UserFromGithub)
  • По типу хранилища (SQLUser, LocalStorageUser)
  • ...

Все это может и будет встречаться в рамках даже одной программы. В зависимости от того, какую задачу мы решаем, может понадобиться разное представление. Наследование не дает такой свободы, наоборот, оно гвоздями приколачивает нас к конкретной структуре, которую уже не поменять. Единственным выходом в рамках этой парадигмы становится еще большее число наследований. В итоге у нас будет комбинация всех возможных поведений, которые встретятся в программе, а это иерархии с десятками и сотнями классов. Не забудьте, что все это каким-то образом должно согласовываться с интерфейсами, которые тоже могут расширять друг друга.

Выходом могло бы быть множественное наследование, но, как показала жизнь некоторых языков (C++), множественное наследование делает все еще сложнее. Поэтому от него отказались все, кто только мог.

В конечном итоге, у разработчиков сформировалась общая позиция по отношению к наследованию, которая звучит так: композиция вместо наследования. Если попробовать загуглить эту фразу, то поисковик покажет невероятное количество статей по этой теме. Этот подход мы уже изучали в курсе «JS: Полиморфизм». Он сводится к более грамотному разделению зон ответственности в приложении, делегированию функциональности другим объектам, нужным в конкретных ситуациях.

С этого момента начинаются сложности. В большинстве статей, посвященных этому вопросу, приводятся либо ошибочные, либо слишком искусственные примеры, которые не дают особого понимания. Для начала отделим две разные причины использования наследования. Одна из них связана с прямым назначением наследования, другая вытекает из неверного понимания принципов организации кода.

Использование не по назначению

Яркий пример использования наследования не по назначению — это смешивание разных уровней абстракции. Выше был пример про пользователей разделяемых по типу хранилища — SQLUser. Возникает вопрос: как пользователь, с точки зрения нашей предметной области, связан с техническими аспектами хранения этих пользователей? Никак не связан, такой код в принципе не должен существовать и наследование для него не предназначено.

Такой код появляется не от незнания ООП, а от непонимания общих принципов организации кода, построения абстракций. Эта тема не является специфичной для ООП, но ООП делает ее сложнее из-за большого числа новых сущностей, которое оно вводит. Обязательно прочитайте эту статью, которая проливает свет на архитектуру.

Парадокс состоит в том, что фраза «composition over inheritance» (композиция вместо наследования) относится именно к такому использованию наследования. То есть проблема не в наследовании как таковом, а в том, что оно оказалось удобным способом организации кода для тех, кто не очень хорошо знает, как его организовывать, что такое барьеры абстракции и слои приложения.

Использование по назначению

Необходимость наследования классов возникает там, где классы связаны общим кодом (это не отношение подтипов). В такой ситуации нужна какая-то альтернатива наследованию.

Решение этой проблемы известно довольно давно и называется миксины. Миксины — настоящая альтернатива правильному использованию наследования.


Дополнительные материалы

  1. Что такое "миксин" в классах JS?

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
от 25 000 ₸ в месяц
Разработка фронтенд-компонентов для веб-приложений
10 месяцев
с нуля
Старт 26 декабря
профессия
от 39 525 ₸ в месяц
Разработка фронтенд- и бэкенд-компонентов для веб-приложений
16 месяцев
с нуля
Старт 26 декабря
профессия
от 25 000 ₸ в месяц
Разработка бэкенд-компонентов для веб-приложений
10 месяцев
с нуля
Старт 26 декабря

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»