Разница между наследованием и композицией
Композиция и наследование - это одно и то же? Если я хочу реализовать шаблон композиции, как я могу это сделать на 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();
}
}
}
Полезно знать :
композиция легко достигается во время выполнения, в то время как наследование предоставляет свои возможности во время компиляции
композиция также известна как отношение HAS-A, а наследование также известно как отношение IS-A
Поэтому возьмите за привычку всегда предпочитать композицию наследованию по различным вышеперечисленным причинам.
Ответ 4
Ответ, данный @Michael Rodrigues, неверен (прошу прощения; я не могу прокомментировать напрямую) и может привести к некоторой путанице.
Реализация интерфейса является формой наследования... когда вы реализуете интерфейс, вы не только наследуете все константы, вы фиксируете, что ваш объект имеет тип, указанный интерфейсом; это по-прежнему отношение "is-a". Если car реализует Fillable, car "is-a" Fillable и может использоваться в вашем коде везде, где вы использовали бы Fillable.
Композиция принципиально отличается от наследования. Когда вы используете композицию, вы (как отмечается в других ответах) устанавливаете связь "имеет" между двумя объектами, в отличие от связи "является", которую вы устанавливаете при использовании наследования.
Итак, исходя из примеров автомобилей в других вопросах, если бы я хотел сказать, что у автомобиля "есть" бензобак, я бы использовал композицию следующим образом:
public class Car {
private GasTank myCarsGasTank;
}
Надеюсь, это прояснит любое недоразумение.