Повторное использование ООП без наследования: насколько практичным является "реальный мир"?

В этой статье описывается подход к ООП, который мне интересен:

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

Идея повторного использования без наследования или зависимости от иерархии классов - это то, что я нашел наиболее поразительным, но насколько это возможно?

Были приведены примеры, но я не могу понять, как я могу изменить свой текущий код, чтобы адаптировать этот подход.

Итак, насколько возможен этот подход? Или действительно не нужно менять код, а скорее сценарийный подход, где "использовать только при необходимости или оптимальном"?

EDIT: oops, я забыл ссылку: здесь ссылка

Ответ 1

Я уверен, что вы слышали о "всегда предпочитаете композицию над наследованием".

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

Основной аргумент в этом заключается в определении принципа замены Лискова и игриво проиллюстрирован этим плакатом:

Liskov Substitution Principle: If it looks like a duck, quacks like a duck, but needs batteries - you probably have the wrong abstraction

Если у вас есть объект ToyDuck, с какого объекта вы должны наследовать, с чисто наследования? Должны ли вы унаследовать от Даки? Нет - скорее всего, вы должны унаследовать игрушку.

В нижней части вы должны использовать правильный метод абстракции - наследование или состав - для вашего кода.

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

Ответ 2

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

  • Классы с унаследованными методами, которые не должны вызываться на них (нарушая принцип замещения Лискова), что смущает программистов и приводит к ошибкам.
  • Глубокие иерархии, где требуется слишком много времени, чтобы найти нужный вам метод, когда он может быть объявлен где угодно в десятках или более классах.

В общем случае дерево наследования не должно быть больше двух или трех уровней, и обычно вам нужно только наследовать интерфейсы и абстрактные базовые классы.

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

Ответ 3

Наследование является фундаментальным

нет наследования, нет ООП.

прототипирование и делегирование могут использоваться для осуществления наследования (например, в JavaScript), что прекрасно, и функционально эквивалентно наследованию

объекты, сообщения и состав, но наследование не основано на объектах, а не объектно-ориентированное. VB5, а не Java. Да, это может быть сделано; план написания большого количества кода шаблона, чтобы разоблачить интерфейсы и операции переадресации.

Те, кто настаивают на наследовании, не нужны, или что это "плохо", создают соломенников: легко представить сценарии, где наследование используется плохо; это не отражение на инструменте, а на инструменте-пользователе.

Ответ 4

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

Что касается утиной печати, я нахожу примеры и мысли сомнительными. Как этот:

function good (foo) {
  if ( !foo.baz || !foo.quux ) {
    throw new TypeError("We need foo to have baz and quux methods.");
  }
  return foo.baz(foo.quux(10));
}

В чем смысл добавления трех новых строк, чтобы сообщить об ошибке, которая будет сообщена автоматически во время выполнения?