Композиция и наследование - это одно и то же? Если я хочу реализовать шаблон композиции, как я могу это сделать на Java?
Переведено автоматически
Ответ 1
Они абсолютно разные. Наследование - это отношение "is-a". Композиция - это отношение "has-a".
Вы создаете композицию, используя экземпляр другого класса C в качестве поля вашего класса, вместо расширения C. Хорошим примером, когда композиция была бы намного лучше, чем наследование, является java.util.Stack, который в настоящее время расширяется java.util.Vector. Сейчас это считается ошибкой. Стек "не является" вектором; вам не должно быть разрешено произвольно вставлять и удалять элементы. Вместо этого должна была быть композиция.
К сожалению, слишком поздно исправлять эту ошибку проектирования, поскольку изменение иерархии наследования сейчас нарушило бы совместимость с существующим кодом. Если Stack использовать композицию вместо наследования, ее всегда можно изменить для использования другой структуры данных без нарушения API.
Я настоятельно рекомендую книгу Джоша Блоха Effective Java 2nd Edition
Пункт 16: Предпочтение композиции перед наследованием
Пункт 17: Разработайте и задокументируйте для наследования или же запретите его
Хороший объектно-ориентированный дизайн - это не либеральное расширение существующих классов. Вашим первым побуждением должно быть создание compose .
Композиция означает HAS A Наследование означает IS A
Example: У автомобиля есть двигатель, а Car - это автомобиль
В программировании это представлено как:
classEngine {} // The Engine class.
classAutomobile {} // Automobile class which is parent to Car class.
classCarextendsAutomobile { // Car is an Automobile, so Car class extends Automobile class. private Engine engine; // Car has an Engine so, Car class has an instance of Engine class as its member. }
Ответ 3
Насколько наследование может быть опасным ?
Давайте рассмотрим пример
publicclassX{ publicvoiddo(){ } } Public Class Y extendsX{ publicvoidwork(){ do(); } }
1) Как ясно из приведенного выше кода, класс Y имеет очень сильную связь с классом X. Если что-то изменится в суперклассе X, Y может резко нарушиться. Предположим, что в будущем класс X реализует метод work с подписью, приведенной ниже.
publicintwork(){ }
Изменение внесено в класс X, но оно сделает класс Y некомпилируемым. ТАКИМ образом, зависимость такого рода может достигать любого уровня и быть очень опасной. Каждый раз, когда суперкласс может не иметь полной видимости кода внутри всех своих подклассов, подкласс может постоянно замечать, что происходит в суперклассе. Итак, нам нужно избегать этой сильной и ненужной связи.
Как композиция решает эту проблему?
Давайте посмотрим, пересмотрев тот же пример
publicclassX{ publicvoiddo(){ } }
Public Class Y{ Xx=newX(); publicvoidwork(){ x.do(); } }
Здесь мы создаем ссылку на класс X в классе Y и вызываем метод класса X путем создания экземпляра класса X. Теперь вся эта сильная связь исчезла. Суперкласс и подкласс теперь в значительной степени независимы друг от друга. Классы могут свободно вносить изменения, которые были опасны в ситуации наследования.
2) Второе очень хорошее преимущество composition в том, что оно обеспечивает гибкость вызова метода, например :
classXimplementsR {} classYimplementsR {}
publicclassTest{ R r; }
В тестовом классе с использованием ссылки r я могу вызывать методы класса X, а также класса Y. Такой гибкости никогда не было в наследовании
3) Еще одно большое преимущество: модульное тестирование
publicclassX { publicvoiddo(){ } }
Public Class Y { Xx=newX(); publicvoidwork(){ x.do(); } }
В приведенном выше примере, если состояние экземпляра x неизвестно, его можно легко смоделировать, используя некоторые тестовые данные, и все методы могут быть легко протестированы. Это вообще было невозможно при наследовании, поскольку вы сильно зависели от суперкласса в получении состояния экземпляра и выполнении любого метода.
4) Еще одна веская причина, по которой нам следует избегать наследования, заключается в том, что Java не поддерживает множественное наследование.
Давайте рассмотрим пример, чтобы понять это :
Public classTransaction { Banking b; publicstaticvoidmain(String a[]) { b = newDeposit(); if(b.deposit()){ b = newCredit(); c.credit(); } } }
Полезно знать :
композиция легко достигается во время выполнения, в то время как наследование предоставляет свои возможности во время компиляции
композиция также известна как отношение HAS-A, а наследование также известно как отношение IS-A
Поэтому возьмите за привычку всегда предпочитать композицию наследованию по различным вышеперечисленным причинам.
Ответ 4
Ответ, данный @Michael Rodrigues, неверен (прошу прощения; я не могу прокомментировать напрямую) и может привести к некоторой путанице.
Реализация интерфейса является формой наследования... когда вы реализуете интерфейс, вы не только наследуете все константы, вы фиксируете, что ваш объект имеет тип, указанный интерфейсом; это по-прежнему отношение "is-a". Если car реализует Fillable, car "is-a" Fillable и может использоваться в вашем коде везде, где вы использовали бы Fillable.
Композиция принципиально отличается от наследования. Когда вы используете композицию, вы (как отмечается в других ответах) устанавливаете связь "имеет" между двумя объектами, в отличие от связи "является", которую вы устанавливаете при использовании наследования.
Итак, исходя из примеров автомобилей в других вопросах, если бы я хотел сказать, что у автомобиля "есть" бензобак, я бы использовал композицию следующим образом: