it-roy-ru.com

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

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

166
gmhk

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

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

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

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

  • Пункт 16: Композиция в пользу наследства
  • Пункт 17: Дизайн и документ для наследования или иначе запретить

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


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

261
polygenelubricants

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

Example: Автомобиль есть Двигатель и Автомобиль это Автомобиль

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

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.
}
170
codaddict

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

Давайте возьмем пример

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

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

public int work(){
}

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

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

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

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

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

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

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

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

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

27
Manoj Kumar Saini

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

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

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

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

public class Car {

private GasTank myCarsGasTank;

}

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

17
Kris

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

public interface Flyable{
 public void fly();
}

public class Duck {
 Flyable fly;

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

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

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");
  }
}

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

14
frictionlesspulley

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

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

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

Таким образом, с помощью нескольких стандартных теоретических компонентов вы можете создать свой объект. Тогда ваша задача - указать, как House защищает своих обитателей и как Car защищает своих обитателей.

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

Например, 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, он может быть заполнен. Конец истории.

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

7
Michael Rodrigues

Состав и наследование одинаковы?

Они не одинаковы. 

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

Наследование : Класс наследует поля и методы от всех своих суперклассов, прямых или косвенных. Подкласс может переопределять методы, которые он наследует, или он может скрывать поля или методы, которые он наследует.

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

Википедия статья достаточно хороша для реализации составного шаблона в Java.

 enter image description here

Ключевые участники:

Составная часть:

  1. Является ли абстракция для всех компонентов, включая составные
  2. Объявляет интерфейс для объектов в состав

Leaf:

  1. Представляет конечные объекты в состав
  2. Реализует все методы Component

Composite:

  1. Представляет составной компонент (компонент, имеющий дочерние элементы)
  2. Реализует методы манипулирования детьми
  3. Реализует все методы компонента, как правило, делегируя их своим дочерним элементам

Пример кода для понимания шаблона Composite:

import Java.util.List;
import Java.util.ArrayList;

interface Part{
    public double getPrice();
    public String getName();
}
class Engine implements Part{
    String name;
    double price;
    public Engine(String name,double price){
        this.name = name;
        this.price = price;
    }
    public double getPrice(){
        return price;
    }
    public String getName(){
        return name;
    }
}
class Trunk implements Part{
    String name;
    double price;
    public Trunk(String name,double price){
        this.name = name;
        this.price = price;
    }
    public double getPrice(){
        return price;
    }
    public String getName(){
        return name;
    }
}
class Body implements Part{
    String name;
    double price;
    public Body(String name,double price){
        this.name = name;
        this.price = price;
    }
    public double getPrice(){
        return price;
    }
    public String getName(){
        return name;
    }
}
class Car implements Part{
    List<Part> parts;
    String name;

    public Car(String name){
        this.name = name;
        parts = new ArrayList<Part>();
    }
    public void addPart(Part part){
        parts.add(part);
    }
    public String getName(){
        return name;
    }
    public String getPartNames(){
        StringBuilder sb = new StringBuilder();
        for ( Part part: parts){
            sb.append(part.getName()).append(" ");
        }
        return sb.toString();
    }
    public double getPrice(){
        double price = 0;
        for ( Part part: parts){
            price += part.getPrice();
        }
        return price;
    }   
}

public class CompositeDemo{
    public static void main(String args[]){
        Part engine = new Engine("DiselEngine",15000);
        Part trunk = new Trunk("Trunk",10000);
        Part body = new Body("Body",12000);

        Car car = new Car("Innova");
        car.addPart(engine);
        car.addPart(trunk);
        car.addPart(body);

        double price = car.getPrice();

        System.out.println("Car name:"+car.getName());
        System.out.println("Car parts:"+car.getPartNames());
        System.out.println("Car price:"+car.getPrice());
    }

}

Результат:

Car name:Innova
Car parts:DiselEngine Trunk Body
Car price:37000.0

Объяснение:

  1. Part это лист
  2. Автомобиль содержит много частей
  3. В автомобиль добавлены различные Parts машины
  4. Цена Автомобиль = сумма (Цена каждого Часть)

Ниже приведен вопрос о преимуществах и недостатках композиции и наследования.

Предпочитаете композицию по наследству?

5
Ravindra babu

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

4
BlackICE

В простом слове Агрегация означает имеет отношения ..  

Композиция является частным случаем агрегации . Более конкретно, ограниченная агрегация называется составом. Когда объект содержит другой объект, если содержащийся объект не может существовать без существования контейнерного объекта, он называется композицией. Пример: класс содержит студентов. Студент не может существовать без класса. Между классом и учениками существует композиция.  

Зачем использовать агрегацию

Повторное использование кода

При использовании агрегации

Повторное использование кода также лучше всего достигается с помощью агрегации, когда нет корабля отношений

Наследование

Наследование - это наследование отношений между родителями и ребенком.

Наследование в Java - это механизм, в котором один объект приобретает все свойства и поведение родительского объекта.

Использование наследования в Java 1 Повторное использование кода . 2 Добавьте дополнительную функцию в дочерний класс, а также переопределение метода (чтобы можно было достичь полиморфизма во время выполнения).

3
Keshav Gera

Наследование между двумя классами, где один класс расширяет другой класс, устанавливает отношения " IS A ". 

Composition на другом конце содержит экземпляр другого класса в вашем классе, устанавливает отношение "Has A". Композиция в Java полезна, поскольку она технически облегчает множественное наследование.

3
John Wilson

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

Наследование - это то, где вы просто берете то, что уже существует, и используете это. Там нет прочных отношений. Человек может унаследовать имущество своего отца, но он может обойтись без него. 

Я не знаю Java, поэтому не могу привести пример, но могу дать объяснение понятий.

2
Robert Rocha

Хотя и Наследование, и Композиция обеспечивают возможность повторного использования кода, основное отличие Композиции от Наследования в Java заключается в том, что Композиция позволяет повторно использовать код без его расширения, но для Наследования вы должны расширить класс для любого повторного использования кода или функциональности. Другое отличие, вытекающее из этого факта, состоит в том, что с помощью Composition вы можете повторно использовать код даже для конечного класса, который не является расширяемым, но Inheritance не может повторно использовать код в таких случаях. Также с помощью Composition вы можете повторно использовать код из многих классов, так как они объявлены как просто переменные-члены, но с помощью Inheritance вы можете повторно использовать код только для одного класса, потому что в Java вы можете расширить только один класс, потому что множественное наследование не поддерживается в Java , Вы можете сделать это в C++, потому что там один класс может расширять более одного класса. Кстати, вы должны всегда предпочитаю композицию наследованию в Java, это не только я, но даже Джошуа Блох предложил в своей книге

2
Nitin Pawar

Я думаю, что этот пример четко объясняет различия между наследование и состав.

В этом примере проблема решается с использованием наследования и композиции. Автор обращает внимание на то, что; в inheritance изменение суперкласса может вызвать проблемы в производном классе, который его наследует.

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

http://www.javaworld.com/article/2076814/core-Java/inheritance-versus-composition--which-one-should-you-choose-.html

1
aunte

Наследование против композиции.

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

Наследование в основном используется в модели программирования семейного алгоритма, такой как тип отношения IS-A, означает похожий тип объекта. Пример.

  1. Дастер это Автомобиль 
  2. Сафари это автомобиль

Они принадлежат семье автомобилей.

Композиция представляет тип отношения HAS-A. Она показывает способность объекта, такого как Duster, имеет пять Gears, Safari имеет четыре Gears и т.д. Всякий раз, когда нам нужно расширить возможности существующего класса, используйте композицию . Пример нам нужно чтобы добавить еще один механизм в объект Duster, мы должны создать еще один объект Gear и скомпоновать его с объектом Duster.

Мы не должны вносить изменения в базовый класс до тех пор, пока/если все производные классы не нуждаются в такой функциональности. Для этого сценария мы должны использовать Composition.Such как

класс A, полученный классом B

Класс A, полученный классом C

Класс A Производный от класса D.

Когда мы добавляем любую функциональность в класс A, она становится доступной для всех подклассов, даже если классы C и D не требуют этих функциональных возможностей. Для этого сценария нам нужно создать отдельный класс для этих функциональных возможностей и объединить его с требуемым классом ( здесь класс B). 

Ниже приведен пример:

          // This is a base class
                 public abstract class Car
                    {
                       //Define prototype
                       public abstract void color();
                       public void Gear() {
                           Console.WriteLine("Car has a four Gear");
                       }
                    }


           // Here is the use of inheritence
           // This Desire class have four gears.
          //  But we need to add one more gear that is Neutral gear.

          public class Desire : Car
                   {
                       Neutral obj = null;
                       public Desire()
                       {
     // Here we are incorporating neutral gear(It is the use of composition). 
     // Now this class would have five gear. 

                           obj = new Neutral();
                           obj.NeutralGear();
                       }

                       public override void color()
                       {
                           Console.WriteLine("This is a white color car");
                       }

                   }


             // This Safari class have four gears and it is not required the neutral
             //  gear and hence we don't need to compose here.

                   public class Safari :Car{
                       public Safari()
                       { }

                       public override void color()
                       {
                           Console.WriteLine("This is a red color car");
                       }


                   }

   // This class represents the neutral gear and it would be used as a composition.

   public class Neutral {
                      public void NeutralGear() {
                           Console.WriteLine("This is a Neutral Gear");
                       }
                   }
1
Sheo Dayal Singh

Наследование означает повторное использование всей функциональности класса. Здесь мой класс должен использовать все методы суперкласса, и мой класс будет аккуратно связан с суперклассом, и код будет продублирован в обоих классах в случае наследования.

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

0
Vikas Kapadiya

Композиция означает создание объекта для класса, который имеет отношение к этому конкретному классу ... Предположим, что учащийся имеет отношение к учетным записям;

Наследование, это предыдущий класс с расширенной функцией. Это означает, что этот новый класс является старым классом с некоторой расширенной функцией. Предположим, что ученик - ученик, но все ученики - люди. Так что есть отношения со студентом и человеком. Это наследство.

0
HM Nayem

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

0
Ranga Reddy

Состав

  • способ объединить простые объекты или типы данных в более сложные
  • Композиционные отношения существуют между двумя классами, если поля одного класса состоят из другого класса.
  • Вызывается как has-a отношение

Наследование

  • На первый взгляд, наследование - это код повторное использование
  • мы можем расширить код, который уже написан управляемым способом.
  • Наследование больше, оно поддерживает полиморфизм на уровне языка!
  • Информация становится управляемой в иерархическом порядке
  • С наследованием новый класс может быть получен из существующих классов как строительный блок
  • Новый класс Наследовать свойства и методы из существующего класса
  • используя расширяет и реализует ключевые слова
  • Существует иерархия классов (Direct/Indirect/Single/Multiple)
0
Malith Ileperuma