Вопрос-ответ

Difference between Inheritance and Composition

Разница между наследованием и композицией

Композиция и наследование - это одно и то же? Если я хочу реализовать шаблон композиции, как я могу это сделать на Java?

Переведено автоматически
Ответ 1

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

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

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

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


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

  • Пункт 17: Разработайте и задокументируйте для наследования или же запретите его

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


Смотрите также:

Ответ 2

Композиция означает HAS A
Наследование означает IS A

Example: У автомобиля есть двигатель, а Car - это автомобиль

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

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.
}
Ответ 3

Насколько наследование может быть опасным ?

Давайте рассмотрим пример

public class X{    
public void do(){
}
}
Public Class Y extends X{
public void work(){
do();
}
}

1) Как ясно из приведенного выше кода, класс Y имеет очень сильную связь с классом X. Если что-то изменится в суперклассе X, Y может резко нарушиться. Предположим, что в будущем класс X реализует метод work с подписью, приведенной ниже.

public int work(){
}

Изменение внесено в класс X, но оно сделает класс Y некомпилируемым. ТАКИМ образом, зависимость такого рода может достигать любого уровня и быть очень опасной. Каждый раз, когда суперкласс может не иметь полной видимости кода внутри всех своих подклассов, подкласс может постоянно замечать, что происходит в суперклассе. Итак, нам нужно избегать этой сильной и ненужной связи.

Как композиция решает эту проблему?

Давайте посмотрим, пересмотрев тот же пример

public class X{
public void do(){
}
}

Public Class Y{
X x = new X();
public void work(){
x.do();
}
}

Здесь мы создаем ссылку на класс X в классе Y и вызываем метод класса X путем создания экземпляра класса X.
Теперь вся эта сильная связь исчезла. Суперкласс и подкласс теперь в значительной степени независимы друг от друга. Классы могут свободно вносить изменения, которые были опасны в ситуации наследования.

2) Второе очень хорошее преимущество composition в том, что оно обеспечивает гибкость вызова метода, например :

class X implements R
{}
class Y implements R
{}

public class Test{
R r;
}

В тестовом классе с использованием ссылки r я могу вызывать методы класса X, а также класса Y. Такой гибкости никогда не было в наследовании

3) Еще одно большое преимущество: модульное тестирование

public class X {
public void do(){
}
}

Public Class Y {
X x = new X();
public void work(){
x.do();
}
}

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

4) Еще одна веская причина, по которой нам следует избегать наследования, заключается в том, что Java не поддерживает множественное наследование.

Давайте рассмотрим пример, чтобы понять это :

Public class Transaction {
Banking b;
public static void main(String a[])
{
b = new Deposit();
if(b.deposit()){
b = new Credit();
c.credit();
}
}
}

Полезно знать :


  1. композиция легко достигается во время выполнения, в то время как наследование предоставляет свои возможности во время компиляции


  2. композиция также известна как отношение HAS-A, а наследование также известно как отношение IS-A


Поэтому возьмите за привычку всегда предпочитать композицию наследованию по различным вышеперечисленным причинам.

Ответ 4

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

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

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

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

public class Car {

private GasTank myCarsGasTank;

}

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

java oop inheritance