Почему не оба?

Эта тема задевает за живое в сообществе разработчиков (в частности, специалисты по ООП метко посоветуют вам отправиться на охоту за утками).

ООП принесло набор приятных маленьких функций (Poly, инкапсуляция и т. д.) и наследование, когда оно было представлено еще в 1990-хs и популяризировано Java в начале 2000-х годов. ООП — это надежный подход к решению реальных проблем. и для некоторых людей (даже для меня) это имеет смысл. Все является классом, у него есть присущие ему функции, вы можете переопределить эти функции, когда этого требует природа, и некоторые другие забавные вещи. Наследование также является безобидной функцией, которая должна была приносить пользу, но с момента ее появления ею жестоко злоупотребляли с помощью глубоко вложенного наследования, которое создает избыточную кодовую базу для более высоких классов, а также проблемы с жесткой связью.

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

Глубокое вложенное наследование также приводит к избыточному коду, нарушающему принцип DRY (Dне Rповторяйте Yнас) .

Это как попросить банан, и вы получите 800-фунтовую разъяренную гориллу, держащую банан, и весь тропический лес вместе с ним.

Хорошо. так что, возможно, наследование — не лучшая идея. Состав?

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

Черт возьми, Джеймс Гослинг, парень, который создал Java, когда его спросили в QA, Если бы вы могли снова сделать Java, что бы вы изменили?, он ответил (цитирую): Я бы исключил классы. . Все засмеялись, но он не шутил (или шутил?). Позже он объяснил, что имел в виду, что глубоко вложенное наследование является проблемой, и с тех пор выступал за реализацию интерфейсов вместо наследования. Звучит разумно, если вы собираетесь наследовать базовый класс только для того, чтобы переопределить каждую проклятую функцию, написанную в нем.

Кто прав? кто знает?

Но что-то по своей сути имеет смысл с композицией. Он выступает за использование меньшего, но более удобного кода. Сосредоточьтесь на том, чточто должен достичь класс, по сравнению с тем, чем он должен быть (внимание, спойлер! люди плохо предсказывают будущее эм>). Используйте компоненты по мере необходимости. Это дает слабую связь, которую можно изменить с небольшими побочными эффектами. Излишне говорить, что Композиция вместо наследования — определенно хорошая практика!

Также прочитайте эту тему, композиция против наследования и это хорошее видео, чтобы лучше понять тему.

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

Так почему бы не использовать и композицию, и наследование?

Позвольте мне проиллюстрировать, скажем, мы разрабатываем классы для покемонов (разберитесь с этим). Основываясь на нашем понимании, теперь мы можем включать те модули (в данном примере объекты класса), которые потребуются в классе, не затрагивая другие классы. Здорово. Итак, теперь в классе будут следующие члены:

и можно использовать так:

Чудесно! Теперь добавляем специи. Не все покемоны созданы равными (спросите Мэджикарпа), некоторые повышают уровень быстрее, чем другие. Поэтому мы пишем разные классы для реализации этого. Но что, если мы реализуем обычное поведение и реализуем специальное поведение, используя унаследованные классы. Это позволит обеспечить как модульность с точки зрения использования, так и детализацию управления с использованием наследования. Опять же, глубокое вложение — это плохо, поэтому нужно быть внимательным.

Итак, наш новый код будет выглядеть так:

и обновите наши файлы покемонов:

и, наконец, использовать его!

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

У этого метода есть следующие преимущества:

  1. Структура наследования теперь обеспечивает гибкость в отношении того, какие классы должны достичь по сравнению с тем, чем они должны быть.
  2. Вы можете удалить эти модули во время выполнения и подключить другой, который соответствует вашим потребностям. (Преимущества слабой связи) [Magikarp (медленно обучающийся) › Gyarados (быстро обучающийся)]

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

Удачного кодирования :)

Мохсин