Благодарить композицию над наследованием

Использовать композицию над наследованием

- очень популярная фраза. Я прочитал несколько статей, и в конце каждой статьи говорится

использовать наследование при наличии чистой связи IS-A между классами.

Пример из этой статьи:

Здесь между Apple и Fruit существует четкое отношение IS-A, т.е. Apple IS-A Fruit, но автор также показал это как Apple HAS-A Fruit ( композиция), чтобы показать ловушку при реализации с наследованием.

Я несколько смутился здесь, что в чем смысл утверждения

использовать наследование при наличии чистой связи IS-A между классами.

Использует ли использование композиции над наследованием означает, что всегда попробовать применять композицию , даже если существует чистая связь IS-A и оставить наследование только для тех случаев, когда композиция не имеет смысла?

Ответ 1

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

Класс java.util.Properties является хорошим примером плохого использования наследования. Вместо использования Hashtable для хранения своих свойств он расширяет Hashtable, чтобы повторно использовать его методы и избегать переопределения некоторых из них с помощью делегирования.

Ответ 2

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

Существует много преимуществ использования композиции, пара из которых:

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

Ответ 3

В статье нет такой фразы:

use inheritance when there is pure IS-A relationship between classes

Кроме того, Google не нашел его в другом месте, кроме вашего сообщения.

Вместо этого статья гласит:

Make sure inheritance models the is-a relationship. My main guiding philosophy is that inheritance should be used only when a subclass is-a superclass. In the example above, an Apple likely is-a Fruit, so I would be inclined to use inheritance.

Это означает, что если есть связь IS_A, попробуйте сначала использовать наследование, а не состав. И нет предпочтения using composition over inheritance - каждый из них хорош для своей собственной роли.

Ответ 4

Кроме того, я хотел бы добавить, что Decorator pattern является примером, где вы можете использовать композицию над наследованием и динамически добавлять обязанности к объектам. Пример шаблона Decorator упоминается в "Эффективной Java" в элементе "Favor Composition over Inheritance". Принять пример из Java API; BufferedReader украшает Reader (вместо расширения FileReader, PipedReader, FilterReader и т.д.) И, следовательно, имеет возможность украшать любой вид Reader. Функциональность буферизации прилагается к этим читателям во время выполнения, которое является шаблоном Decorator.

Здесь расширение путем подклассификации было бы непрактичным.

Ответ 5

Я предполагаю, что хорошим ориентиром будет:

Когда есть связь IS-A, используйте наследование. В противном случае используйте Состав.

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

Чтобы проиллюстрировать, представьте, есть ли у вас функция, которая принимает тип животного. Когда вы используете наследование, у вас есть только одна функция:

void feed( Animal a );

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

Если нет отношения IS-A, я думаю, что полиморфизм не будет очень эффективным. Поэтому было бы лучше использовать композицию и улучшать инкапсуляцию/изоляцию между классами.

Ответ 6

Используйте наследование, когда связь является постоянной. Не используйте расширение, чтобы получить свободное поведение. Это то, на что они ссылаются в примере IS-A. Расширяйтесь только в том случае, если расширяющийся класс действительно является расширением родителя. Будут моменты, когда он не режет и не сушит, а вообще. Композиция обеспечивает гибкость в будущем, если вам нужно перейти от "правого" класса.

Это старая, но отличная статья о расширении:

http://www.javaworld.com/javaworld/jw-08-2003/jw-0801-toolbox.html

Ответ 7

Я бы сказал, нет. В сценарии "фрукты/яблоко" имеет смысл использовать наследование. Автор статьи также подтверждает это: "В приведенном выше примере Apple, вероятно, является" фруктом ", поэтому я бы склонен использовать наследование".

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