Различие между наследованием и составом

Действительно ли Состав и Наследование являются тем же? Если я хочу реализовать шаблон состава, как я могу сделать это в Java?

187
задан Péter Török 28 June 2010 в 04:38
поделиться

6 ответов

Они абсолютно разные. Наследование - это "есть-есть" отношения. Композиция - это "has-a".

Вы выполняете композицию, имея экземпляр другого класса C в качестве поля вашего класса, вместо того, чтобы расширять C. Хорошим примером, когда композиция была бы намного лучше наследования, является java.util.Stack, который в настоящее время расширяет java.util.Vector. Сейчас это считается ошибкой. Стек "is-NOT-a" вектор; вам не должно быть позволено вставлять и удалять элементы произвольно. Вместо этого должна была быть композиция.

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

Я очень рекомендую книгу Джоша Блоха Effective Java 2nd Edition

  • Пункт 16: Предпочитайте композицию наследованию
  • Пункт 17: Проектируйте и документируйте наследование, иначе запретите его

Хороший объектно-ориентированный дизайн - это не свободное расширение существующих классов. Ваш первый инстинкт должен быть направлен на композицию.


См. также:

292
ответ дан 23 November 2019 в 05:44
поделиться

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

4
ответ дан 23 November 2019 в 05:44
поделиться

Композиция - это то же самое, что и звучит: вы создаете объект, соединяя части.

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

Car implements iDrivable, iUsesFuel, iProtectsOccupants
Motorbike implements iDrivable, iUsesFuel, iShortcutThroughTraffic
House implements iProtectsOccupants
Generator implements iUsesFuel

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

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

Например, MotorVehicle может иметь метод Fuelable и метод Drive. Вы можете оставить метод Fuel как есть, потому что заправлять мотоцикл и автомобиль одинаково, но вы можете переопределить метод Drive, потому что мотоцикл управляется совсем иначе, чем Car.

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

Композиция считается более гибкой, потому что если у вас есть такой метод, как iUsesFuel, вы можете иметь метод в другом месте (в другом классе, в другом проекте), который просто заботится о работе с объектами, которые могут быть заправлены, независимо от того, автомобиль это, лодка, плита, барбекю и т. д. Интерфейсы требуют, чтобы классы, которые говорят, что они реализуют этот интерфейс, на самом деле имели методы, о которых идет речь в этом интерфейсе. Например,

iFuelable Interface:
   void AddSomeFuel()
   void UseSomeFuel()
   int  percentageFull()

тогда вы можете иметь метод в другом месте

private void FillHerUp(iFuelable : objectToFill) {

   Do while (objectToFill.percentageFull() <= 100)  {

        objectToFill.AddSomeFuel();
   }

Странный пример, но он показывает, что этому методу все равно, что он заправляет, потому что объект реализует iUsesFuel, он может быть заправлен. Конец истории.

Если бы вы использовали наследование, вам бы понадобились разные методы FillHerUp для работы с MotorVehicles и Barbecues, если только у вас не было какого-нибудь довольно странного базового объекта "ObjectThatUsesFuel", от которого можно было бы наследоваться.

7
ответ дан 23 November 2019 в 05:44
поделиться

Композиция означает, что ИМЕЕТ A
Средство наследования IS A

Пример : Автомобиль имеет Двигатель и автомобиль - это автомобиль

В программировании это представлено как:

class Engine {} // The Engine class.

class Automobile {} // Automobile class which is parent to Car class.

class Car extends Automobile { // 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.
}
196
ответ дан 23 November 2019 в 05:44
поделиться

Ответ @Michael Rodrigues неверен (прошу прощения; я не могу комментировать напрямую) и может привести к некоторой путанице.

Реализация интерфейса - это форма наследования ... когда вы реализуете интерфейс, вы не только наследуете все константы, но и передаете свой объект типу, указанному в интерфейсе; это все еще связь " is-a ". Если автомобиль реализует Fillable , автомобиль « is-a » Fillable , и его можно использовать в коде везде, где вы использовали бы Fillable .

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

Итак, из примеров автомобилей в других вопросах, если бы я хотел сказать, что автомобиль « имеет » бензобак, я бы использовал следующий состав:

public class Car {

private GasTank myCarsGasTank;

}

Надеюсь, это проясняет до любого недоразумения.

19
ответ дан 23 November 2019 в 05:44
поделиться

Наследование выявляет отношение IS-A . Композиция выявляет отношение HAS-A . Шаблон стратегии объясняет, что композицию следует использовать в случаях, когда есть семейства алгоритмов, определяющие конкретное поведение.
Классический пример класса утки, который реализует поведение полета.

public interface Flyable{
 public void fly();
}

public class Duck {
 Flyable fly;

 public Duck(){
  fly = new BackwardFlying();
 }
}

Таким образом, у нас может быть несколько классов, реализующих полет. например:

public class BackwardFlying implements Flyable{
  public void fly(){
    Systemout.println("Flies backward ");
  }
}
public class FastFlying implements Flyable{
  public void fly(){
    Systemout.println("Flies 100 miles/sec");
  }
}

Если бы это было наследование, у нас было бы два разных класса птиц, которые снова и снова реализуют функцию полета. Так что наследование и состав совершенно разные.

17
ответ дан 23 November 2019 в 05:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: