Різниця між спадковістю та складом


208

Чи однаковий склад і спадщина? Якщо я хочу реалізувати шаблон композиції, як це зробити на Java?


2
Інше пов’язане запитання: чи є щось, що може не досягти цього спадку? stackoverflow.com/questions/2238642 / ...
ewernli

1
Також дивіться

1
Ця стаття була корисна для мене і так допомогли: thoughtworks.com/insights/blog / ...
Qmaster

Відповіді:


321

Вони абсолютно різні. Спадщина - це "є-а" відносини. Склад - це «є-є» .

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

На жаль, пізно виправити цю помилку дизайну, оскільки зміна ієрархії спадкування тепер порушить сумісність із існуючим кодом. Якщо Stackвикористовується композиція замість успадкування, її завжди можна модифікувати для використання іншої структури даних без порушення API .

Я настійно рекомендую книгу Джоша Блоха « Ефективна Java 2-го видання»

  • Пункт 16: Склад прихильності над спадщиною
  • Пункт 17: Дизайн та документ на спадщину або інше забороняють

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


Дивитися також:


4
Цікаво. Чому б просто не створити новий клас java.util.Stack2, який використовує композицію?
qed

5
Я ціную цю відповідь; однак я відчуваю, ніби відповідь не виходить за межі і більше заглиблюється в питання щодо дизайну мови (та певного пакету), ніж відповідає на задане питання щодо складу та спадкування. Я великий прихильник відповіді на питання щодо ПЗ, посилаючись на ресурси - не посилаючись на зовнішні ресурси, не надаючи більш глибокого резюме, ніж однорядне підсумовування.
Томас

5
Поганий приклад, мені довелося провести додатковий пошук, щоб зрозуміти, що таке вектор і стек.
Сем Рамезанлі

Добре , але ваш приклад про стек Java не є хорошим кандидатом з - за цієї спадщини є зразком поганого рішення про вибір спадкування за складом , як сказано в цій статті: thoughtworks.com/insights/blog / ...
Qmaster

212

Склад означає 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.
}

7
Я б змінив "автомобіль" на "автомобіль, що працює на двигуні", як і в багатьох інтерпретаціях, автомобілі та автомобілі еквівалентні.
нанофарад

5
@hexafraction Я погоджуюся з тим, що транспортний засіб, ймовірно, буде кращим вибором, але в той же час - рішення суду - продемонструвало непогашене питання про те, що просили.
nckbrz

5
"двигун":-/
Омар Тарік

добре розширився. +1. подальше читання javarevisited.blogspot.in/2015/06/…
roottraveller

@AndreyAkhmetov Автомобіль може мати typeполе типуEnum
Ojonugwa Jude Ochalifu

42

Наскільки спадщина може бути небезпечною?

Давайте візьмемо приклад

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 невідмінним. Тож така залежність може піднятися на будь-який рівень, і це може бути дуже небезпечно. Кожен раз, коли суперклас може не мати повної видимості для кодування у всіх його підкласах і підкласах, можна постійно помічати, що відбувається в надкласі. Тому нам потрібно уникати цієї сильної і непотрібної зв'язку.

Як композиція вирішує це питання?

Подивимось, переглянувши той же приклад

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

Тому зробіть звичку завжди віддавати перевагу складу над спадщиною з різних вищезазначених причин.


3
згоден, але з огляду на ваше рішення, використовуючи склад .... нам все одно потрібно зробити няню, наприклад, зараз суперкласс X змінити ім'я методу з "робити на робити" .... тоді підклас Y також потрібно буде підтримувати (потрібно змінити а) це все-таки щільна муфта? І як ми його позбудемося?
Застряглий потік

19

Відповідь, надана @Michael Rodrigues, є невірною (прошу вибачення; я не можу коментувати прямо), і могла призвести до певної плутанини.

Реалізація інтерфейсу - це форма успадкування ... коли ви реалізуєте інтерфейс, ви не тільки успадковуєте всі константи, ви здійснюєте об'єкт такого типу, який визначений інтерфейсом; це все-таки стосунки " є-а ". Якщо автомобіль реалізує Fillable , автомобіль " є-a " Задається , і його можна використовувати у вашому коді, де б ви не використовували Fillable .

Склад принципово відрізняється від спадкового. Коли ви використовуєте композицію, ви (як відзначають інші відповіді) створюєте відносини " has-a " між двома об'єктами, на відміну від відносини " є-а ", яке ви створюєте , коли використовуєте спадщину .

Отже, з прикладів автомобіля в інших питаннях, якби я хотів сказати, що автомобіль має "цистерну" з бензином, я б використав склад таким чином:

public class Car {

private GasTank myCarsGasTank;

}

Сподіваємось, це усуне будь-яке непорозуміння.


17

Спадщина виявляє відношення 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");
  }
}

Якби це було у спадок, у нас було б два різних класу птахів, які реалізовують функцію мухи знову і знову. Тож успадкування та склад абсолютно різні.


7

Композиція така, як звучить - ви створюєте об’єкт, підключаючи частини.

Редагуйте решту цієї відповіді помилково, виходячи з наступної передумови.
Це здійснюється за допомогою інтерфейсів.
Наприклад, використовуючи Carприклад вище,

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

Тож за допомогою декількох стандартних теоретичних компонентів ви можете створити свій об’єкт. Тоді ваша робота заповнити, як Houseзахищає своїх мешканців, і як Carзахищає своїх мешканців.

Спадщина - це як навпаки. Ви починаєте з повного (або напівповне) об’єкта і замінюєте або переосмислюєте різні біти, які хочете змінити.

Наприклад, MotorVehicleможе бути використаний Fuelableметод і Driveметод. Ви можете залишити метод палива таким, яким він є, тому що це те саме, щоб заповнити мотоцикл і автомобіль, але ви можете перекрити 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", від якого слід успадкувати.


У конвенціях Java зазначено, що імена класів та інтерфейсів записуються в ThisCase, а не в camelCase. Тому найкраще називати свої інтерфейси IDrivableтощо. Можливо, вам не знадобиться "Я", якщо ви перегрупуєте всі свої інтерфейси в пакет правильно.
ThePyroEagle

6

Чи однаковий склад і спадщина?

Вони не однакові.

Склад : Це дає змогу групі об'єктів поводитися так само, як і окремий екземпляр об'єкта. Метою композиту є "складання" об'єктів у структурах дерев, щоб представити ієрархії частини цілого

Спадщина : клас успадковує поля та методи від усіх своїх суперклассів, прямих чи непрямих. Підклас може змінювати методи, які він успадковує, або може приховати поля чи методи, які він успадковує.

Якщо я хочу реалізувати шаблон композиції, як це зробити на Java?

Стаття у Вікіпедії досить хороша, щоб реалізувати композитний малюнок у Java.

введіть тут опис зображення

Основні учасники:

Компонент :

  1. Це абстракція для всіх компонентів, у тому числі композиційних
  2. Оголошує інтерфейс для об’єктів у композиції

Лист :

  1. Представляє в композиції листові об’єкти
  2. Реалізує всі методи компонентів

Композит :

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

Приклад коду для розуміння складеного шаблону:

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. Частина - лист
  2. Автомобіль містить багато деталей
  3. До автомобіля додано різні частини автомобіля
  4. Ціна автомобіля = сума (ціна кожної деталі )

До плюсів і мінусів складу та успадкування див. Нижче питання.

Віддаєте перевагу складу над спадщиною?


Чи має сенс реалізовувати деталі і для автомобільного класу. Не буде добре, якщо використовувати його як композицію
balkkian

4

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


4

Композиція - це те, де щось складається з різних частин, і воно має тісний зв’язок з цими частинами. Якщо основна частина помирає так, як і інші, вони не можуть мати власне життя. Грубий приклад - людський організм. Вийняти серце, і всі інші частини відмирають.

Спадщина - це те, коли ти просто береш щось, що вже існує, і використовуєш його. Сильних стосунків немає. Людина могла успадкувати майно своїх батьків, але він може обійтися і без цього.

Я не знаю Java, тому не можу навести приклад, але можу надати пояснення понять.


3

Спадкування між двома класами, коли один клас поширює інший клас встановлює " IS A ".

Композиція на іншому кінці містить екземпляр іншого класу у вашому класі, встановлює відносини " Має А ". Композиція в java корисна, оскільки технічно полегшує багаторазове успадкування.


3

У простому слові агрегації означає, що має стосунки ..

Склад - особливий випадок агрегації . Більш конкретним способом обмежена агрегація називається композицією. Якщо об'єкт містить інший об'єкт, якщо об'єкт, що міститься, не може існувати без існування об'єкта контейнера, він називається складом. Приклад: Клас містить учнів. Студент не може існувати без класу. Існує склад між класом і студентами.

Навіщо використовувати агрегацію

Використання коду

При використанні агрегації

Повторне використання коду також найкраще досягається шляхом агрегування, коли немає відношення

Спадщина

Спадщина - це стосунки батьківських батьків. Значення спадщини - це відносини

Спадщина в java - це механізм, за допомогою якого один об'єкт набуває всіх властивостей і поведінки материнського об'єкта.

Використання успадкування в Java 1 Код повторної використання. 2 Додайте додаткову особливість у дитячому класі, а також переосмислення методу (так можна досягти поліморфізму виконання).


1

Хоча і Inheritance, і Composition надають можливість повторної використання коду, головна відмінність Composition від Inheritance у Java полягає в тому, що Composition дозволяє повторно використовувати код, не розширюючи його, але для Inheritance ви повинні розширити клас для будь-якого повторного використання коду чи функціональності. Ще одна відмінність, що випливає з цього факту, полягає в тому, що за допомогою композиції ви можете повторно використовувати код навіть для остаточного класу, який не розширюється, але Спадщина не може повторно використовувати код у таких випадках. Також за допомогою композиції ви можете повторно використовувати код з багатьох класів, оскільки вони оголошені просто змінною члена, але за допомогою Inheritance ви можете повторно використовувати кодову форму лише одного класу, тому що в Java ви можете розширити лише один клас, тому що множинне спадкування не підтримується в Java . Це можна зробити в C ++, хоча там один клас може поширювати більше одного класу. До речі, завжди слідвіддайте перевагу композиції над спадщиною на Java , її не тільки я, але навіть Джошуа Блох запропонував у своїй книзі


1
Чому я повинен «вважати склад над успадкуванням »? Вони є різними поняттями і використовуються для різних цілей. Чесно кажучи, я не бачу, як ви могли навіть переходити від одного до іншого.
Крьов

1

Я думаю, що цей приклад чітко пояснює різницю між спадщиною та складом .

У цьому прикладі проблема вирішується за допомогою успадкування та складу. Автор звертає увагу на те, що; в успадкуванні зміна суперкласу може спричинити проблеми у похідному класі, які успадковують його.

Там ви також можете побачити різницю в представленні, коли ви використовуєте UML для успадкування або складу.

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


1
Відповіді лише на посилання відмовляють, оскільки вони старі. Будь ласка, включіть у відповідь принаймні найважливішу відповідну інформацію за посиланням.
Скотт Солмер

1

Спадкові Vs Склад.

Спадковість і склад використовуються для повторної використання та розширення поведінки класу.

Спадки в основному використовують в моделі програмування сімейного алгоритму, такі як тип відношення IS-A означає об'єкт подібного типу. Приклад.

  1. Дастер - автомобіль
  2. Сафарі - автомобіль

Ці належать до родини автомобілів.

Склад являє собою тип відносин HAS-A. Він показує здатність такого об'єкта, як Duster має п'ять передач, Safari чотири Gears і т.д. Приклад: нам потрібно додати ще одну передачу в об'єкт Duster, тоді ми повинні створити ще один редуктор і скласти його до об'єкта пилу.

Ми не повинні вносити зміни до базового класу до тих пір, поки всі похідні класи не потребують цієї функціональності. Для цього сценарію ми повинні використовувати Composition.Such as

клас А Похідне класом В

Клас А Похідний класом С

Клас A Похідний класом D.

Коли ми додаємо будь-яку функціональність до класу A, він доступний для всіх підкласів, навіть коли для класів C і D ці функції не потребують. Для цього сценарію нам потрібно створити окремий клас для цих функціональних можливостей і скласти його до необхідного класу ( ось клас В).

Нижче наведено приклад:

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

0

Композиція означає створення об'єкта для класу, який має відношення до цього конкретного класу. Припустимо, студент має відношення до облікових записів;

Спадщина - це попередній клас із розширеною функцією. Це означає, що цей новий клас - це Старий клас з деякою розширеною особливістю. Припустимо, студент - студент, але всі студенти - люди. Отже, існує зв’язок зі студентом і людиною. Це наслідування.


0

Ні, обидва різні. Склад дотримується відносин "HAS-A", а спадкування - "IS-A". Найкращим прикладом для композиції був Стратегічний зразок.


2
Єдина його якість коментарів
Mathews Sunny

Ви просто прокоментували те саме, що прийняла відповідь, сказана за 7 років до вас?
Анжіль Дхамала

0

Спадщина означає повторне використання повної функціональності класу. Тут мій клас повинен використовувати всі методи суперкласу, і мій клас буде тісно поєднаний із суперкласом, і код буде дублюватися в обох класах у разі спадкування.

Але ми можемо подолати всі ці проблеми, коли використовуємо композицію для розмови з іншим класом. композиція оголошує атрибут іншого класу в моєму класі, з яким ми хочемо поговорити. і яку функціональність ми хочемо отримати від цього класу, використовуючи цей атрибут.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.