Differenza tra ereditarietà e composizione


208

Composizione ed ereditarietà sono uguali? Se voglio implementare il modello di composizione, come posso farlo in Java?


2
Altra domanda correlata: c'è qualcosa che la composizione non può realizzare come può l'eredità? stackoverflow.com/questions/2238642/...
ewernli

1
Vedi anche is-a-vs-has-a-
what

1
Questo articolo mi è stato utile e mi è stato di grande aiuto: thoughtworks.com/insights/blog/…
QMaster

Risposte:


321

Sono assolutamente diversi. L'ereditarietà è una relazione "is-a" . La composizione è una "has-a" .

Fai composizione avendo un'istanza di un'altra classe Ccome campo della tua classe, invece di estenderla C. Un buon esempio in cui la composizione sarebbe stata molto meglio dell'eredità è java.util.Stack, che attualmente si estende java.util.Vector. Questo è ora considerato un errore. Uno stack "NON-NON-un" vettore; non dovresti essere autorizzato a inserire e rimuovere elementi arbitrariamente. Avrebbe dovuto essere invece composizione.

Sfortunatamente è troppo tardi per correggere questo errore di progettazione, poiché la modifica della gerarchia dell'ereditarietà comprometterebbe la compatibilità con il codice esistente. Aveva Stackutilizzato la composizione anziché l'ereditarietà, può sempre essere modificata per utilizzare un'altra struttura di dati senza violare l'API .

Consiglio vivamente il libro di Josh Bloch, Effective Java 2nd Edition

  • Articolo 16: favorisce la composizione rispetto all'eredità
  • Articolo 17: progettare e documentare l'ereditarietà oppure vietarlo

Una buona progettazione orientata agli oggetti non riguarda l'estensione libera delle classi esistenti. Il tuo primo istinto dovrebbe essere quello di comporre invece.


Guarda anche:


4
Interessante. Perché non creare semplicemente una nuova classe java.util.Stack2 che utilizza la composizione?
Q

5
Apprezzo questa risposta; tuttavia, ho la sensazione che la risposta vada fuori strada e approfondisca maggiormente le preoccupazioni relative al design della lingua (e ad un pacchetto particolare) rispetto a quanto non risponda alla domanda posta sulla composizione rispetto all'eredità. Sono un grande fan di rispondere alla domanda su SO, citando risorse - non collegandomi a risorse esterne senza fornire un riepilogo più approfondito di un riassunto di una riga.
Thomas,

5
Cattivo esempio, ho dovuto fare ulteriori ricerche per capire cosa sono Vector e Stack.
Sam Ramezanli,

Buono ma il tuo esempio di stack Java non è un buon candidato a causa di questa eredità è un esempio di cattiva decisione sulla scelta dell'ereditarietà rispetto alla composizione, come detto in questo articolo: thoughtworks.com/insights/blog/…
QMaster

212

Composizione significa HAS A
ereditarietàIS A

Example: Auto ha un motore e auto è un Automobile

Nella programmazione questo è rappresentato come:

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
Vorrei cambiare "automobile" in "veicolo a motore", poiché in molte interpretazioni le automobili e le automobili sono equivalenti.
nanofarad,

5
@hexafraction Sono d'accordo sul fatto che il veicolo sarebbe probabilmente una scelta migliore, ma allo stesso tempo -codaddict- ha illustrato il punto giusto per quello che è stato chiesto.
nckbrz,

5
"un motore":-/
Omar Tariq,

ben espulso. +1. ulteriori letture javarevisited.blogspot.in/2015/06/…
roottraveller

@AndreyAkhmetov L'automobile può avere un typecampo di tipoEnum
Ojonugwa Jude Ochalifu,

42

In che modo l'eredità può essere pericolosa?

Facciamo un esempio

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

1) Come indicato nel codice sopra, la Classe Y ha un accoppiamento molto forte con la Classe X. Se qualcosa cambia nella superclasse X, Y potrebbe rompersi drammaticamente. Supponiamo che in futuro la classe X attui un metodo di lavoro con la seguente firma

public int work(){
}

Il cambiamento viene effettuato nella classe X ma renderà la classe Y incompatibile. Quindi questo tipo di dipendenza può salire a qualsiasi livello e può essere molto pericoloso. Ogni volta che la superclasse potrebbe non avere la piena visibilità del codice all'interno di tutte le sue sottoclassi e la sottoclasse può continuare a notare ciò che sta accadendo nella superclasse per tutto il tempo. Quindi dobbiamo evitare questo accoppiamento forte e inutile.

In che modo la composizione risolve questo problema?

Vediamo rivedendo lo stesso esempio

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

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

Qui stiamo creando il riferimento della classe X nella classe Y e invocando il metodo della classe X creando un'istanza della classe X. Ora tutto quel forte accoppiamento è sparito. Superclass e sottoclasse sono altamente indipendenti l'uno dall'altro ora. Le classi possono liberamente apportare modifiche che erano pericolose nella situazione ereditaria.

2) Secondo ottimo vantaggio della composizione in quanto offre flessibilità di metodo di chiamata, ad esempio:

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

public class Test{    
    R r;    
}

Nella classe Test usando il riferimento r posso invocare i metodi della classe X e della classe Y. Questa flessibilità non è mai stata lì in eredità

3) Un altro grande vantaggio: test unitari

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

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

Nell'esempio sopra, se lo stato dell'istanza x non è noto, può essere facilmente simulato usando alcuni dati di test e tutti i metodi possono essere facilmente testati. Questo non era affatto possibile in eredità poiché eri fortemente dipendente dalla superclasse per ottenere lo stato di istanza ed eseguire qualsiasi metodo.

4) Un altro buon motivo per cui dovremmo evitare l'ereditarietà è che Java non supporta l'ereditarietà multipla.

Facciamo un esempio per capirlo:

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

Buono a sapersi :

  1. la composizione è facilmente raggiungibile in fase di esecuzione mentre l'ereditarietà fornisce le sue funzionalità in fase di compilazione

  2. la composizione è anche conosciuta come relazione HAS-A e l'eredità è anche nota come relazione IS-A

Quindi prendi l'abitudine di preferire sempre la composizione all'eredità per vari motivi di cui sopra.


3
d'accordo ma data la tua soluzione usando la composizione .... abbiamo ancora bisogno di fare una babysitter, ad esempio ora la super classe X cambia il nome del metodo da do a do .... quindi anche la sottoclasse Y dovrà essere mantenuta (deve essere cambiata anche) questo è ancora accoppiamento stretto? E come possiamo liberarcene?
stuckedoverflow

19

La risposta data da @Michael Rodrigues non è corretta (chiedo scusa; non sono in grado di commentare direttamente) e potrebbe creare confusione.

L'implementazione dell'interfaccia è una forma di ereditarietà ... quando si implementa un'interfaccia, non si eredita solo tutte le costanti, si impegna il proprio oggetto ad essere del tipo specificato dall'interfaccia; è ancora una relazione " is-a ". Se un'auto implementa Fillable , l'auto " è-a " Fillable e può essere utilizzata nel tuo codice ovunque tu utilizzi un Fillable .

La composizione è sostanzialmente diversa dall'eredità. Quando usi la composizione, stai (come notano le altre risposte) creando una relazione " has-a " tra due oggetti, al contrario della relazione " is-a " che crei quando usi l'ereditarietà .

Quindi, dagli esempi di auto nelle altre domande, se volessi dire che un'auto " ha " un serbatoio di gas, userei la composizione, come segue:

public class Car {

private GasTank myCarsGasTank;

}

Spero che questo chiarisca ogni equivoco.


17

L'ereditarietà mette in evidenza la relazione IS-A . La composizione mette in evidenza la relazione HAS-A . Il modello di strategia spiega che la composizione dovrebbe essere usata nei casi in cui esistono famiglie di algoritmi che definiscono un comportamento particolare.
Esempio classico di una classe di anatre che implementa un comportamento di volo.

public interface Flyable{
 public void fly();
}

public class Duck {
 Flyable fly;

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

Quindi possiamo avere più classi che implementano il volo, ad esempio:

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

Se fosse stato per eredità, avremmo due diverse classi di uccelli che implementano ripetutamente la funzione fly. Quindi eredità e composizione sono completamente diverse.


7

La composizione è come sembra: si crea un oggetto collegando le parti.

MODIFICA il resto di questa risposta si basa erroneamente sulla premessa seguente.
Questo si ottiene con le interfacce.
Ad esempio, usando l' Caresempio sopra,

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

Quindi con alcuni componenti teorici standard puoi costruire il tuo oggetto. È quindi il tuo lavoro compilare il modo in cui Houseprotegge i suoi occupanti e come Carprotegge i suoi occupanti.

L'ereditarietà è come il contrario. Inizi con un oggetto completo (o semi-completo) e sostituisci o Sostituisci i vari bit che vuoi cambiare.

Ad esempio, MotorVehiclepuò venire con un Fuelablemetodo e un Drivemetodo. È possibile abbandonare il metodo Fuel in quanto è lo stesso per riempire una moto e un'auto, ma è possibile ignorare il Drivemetodo perché la motocicletta guida in modo molto diverso da unCar .

Con l'ereditarietà, alcune classi sono già completamente implementate e altre hanno metodi che sei costretto a scavalcare. Con Composizione non ti viene dato niente. (ma puoi implementare le interfacce chiamando metodi in altre classi se ti capita di avere qualcosa in giro).

La composizione è vista come più flessibile, perché se hai un metodo come iUsesFuel, puoi avere un metodo da qualche altra parte (un'altra classe, un altro progetto) che si preoccupa solo di gestire oggetti che possono essere alimentati, indipendentemente dal fatto che sia un'auto, barca, fornello, barbecue, ecc. Le interfacce richiedono che le classi che affermano di implementare quell'interfaccia abbiano effettivamente i metodi su cui quell'interfaccia si basa. Per esempio,

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

allora puoi avere un metodo da qualche altra parte

private void FillHerUp(iFuelable : objectToFill) {

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

        objectToFill.AddSomeFuel();
   }

Strano esempio, ma mostra che a questo metodo non importa cosa si sta riempiendo, poiché l'oggetto implementa iUsesFuel, può essere riempito. Fine della storia.

Se invece avessi usato Ereditarietà, avresti bisogno di FillHerUpmetodi diversi per gestire MotorVehiclese Barbecues, a meno che tu non avessi qualche oggetto base "ObjectThatUsesFuel" piuttosto strano da cui ereditare.


Le convenzioni Java affermano che i nomi di classe e interfaccia sono scritti in ThisCase, non in camelCase. Pertanto è meglio nominare le interfacce IDrivable, ecc. Potrebbe non essere necessario l'io se si raggruppano correttamente tutte le interfacce in un pacchetto.
ThePyroEagle il

6

Composizione ed ereditarietà sono uguali?

Non sono uguali.

Composizione : consente a un gruppo di oggetti di essere trattato allo stesso modo di una singola istanza di un oggetto. L'intento di un composito è di "comporre" oggetti in strutture ad albero per rappresentare gerarchie intere

Eredità : una classe eredita campi e metodi da tutte le sue superclassi, dirette o indirette. Una sottoclasse può ignorare i metodi che eredita oppure può nascondere campi o metodi che eredita.

Se voglio implementare il modello di composizione, come posso farlo in Java?

L' articolo di Wikipedia è abbastanza buono per implementare pattern compositi in Java.

inserisci qui la descrizione dell'immagine

Partecipanti chiave:

Componente :

  1. È l'astrazione per tutti i componenti, compresi quelli compositi
  2. Dichiara l'interfaccia per gli oggetti nella composizione

Foglia :

  1. Rappresenta gli oggetti foglia nella composizione
  2. Implementa tutti i metodi Component

Composito :

  1. Rappresenta un componente composito (componente con figli)
  2. Implementa metodi per manipolare i bambini
  3. Implementa tutti i metodi Component, generalmente delegandoli ai suoi figli

Esempio di codice per comprendere il modello composito :

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

}

produzione:

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

Spiegazione:

  1. La parte è una foglia
  2. L'auto contiene molte parti
  3. Diverse parti dell'auto sono state aggiunte all'auto
  4. Il prezzo di Auto = somma di (Prezzo di ogni Parte )

Fare riferimento alla domanda di seguito per Pro e contro di composizione ed ereditarietà.

Preferisci la composizione rispetto all'eredità?


Ha senso implementare parti anche per la classe di auto. Non sarà buono se lo usi come composizione
bhalkian

4

come altro esempio, considera una classe di auto, questo sarebbe un buon uso della composizione, un'auto "avrebbe" un motore, una trasmissione, pneumatici, sedili, ecc. Non estenderebbe nessuna di quelle classi.


4

La composizione è il punto in cui qualcosa è composto da parti distinte e ha una forte relazione con quelle parti. Se la parte principale muore, così fanno gli altri, non possono avere una vita propria. Un esempio approssimativo è il corpo umano. Elimina il cuore e tutte le altre parti muoiono.

L'ereditarietà è dove prendi qualcosa che esiste già e lo usi. Non esiste una relazione forte. Una persona potrebbe ereditare la proprietà del padre, ma può farne a meno.

Non conosco Java, quindi non posso fornire un esempio ma posso fornire una spiegazione dei concetti.


3

Ereditarietà tra due classi, in cui una classe estende un'altra classe stabilisce la relazione " IS A ".

La composizione all'altra estremità contiene un'istanza di un'altra classe nella tua classe stabilisce una relazione " Ha A ". La composizione in Java è utile poiché facilita tecnicamente l'ereditarietà multipla.


3

In Simple Word Aggregation significa che ha una relazione.

La composizione è un caso speciale di aggregazione . In un modo più specifico, un'aggregazione limitata è chiamata composizione. Quando un oggetto contiene l'altro oggetto, se l'oggetto contenuto non può esistere senza l'esistenza di un oggetto contenitore, allora si chiama composizione. Esempio: una classe contiene studenti. Uno studente non può esistere senza una lezione. Esiste una composizione tra classe e studenti.

Perché usare l'aggregazione

Riutilizzabilità del codice

Quando si utilizza l'aggregazione

Il riutilizzo del codice si ottiene anche meglio dall'aggregazione quando non esiste una nave relazione

Eredità

L'ereditarietà è una relazione genitore figlio L'ereditarietà è una relazione

L'ereditarietà in Java è un meccanismo in cui un oggetto acquisisce tutte le proprietà e i comportamenti dell'oggetto genitore.

Utilizzo dell'ereditarietà nella riusabilità del codice Java 1. 2 Aggiungi funzionalità extra nella classe figlio e metodo Override (in modo da poter ottenere il polimorfismo di runtime).


1

Sebbene sia l'ereditarietà sia la composizione forniscano riusabilità del codice, la principale differenza tra composizione ed ereditarietà in Java è che Composizione consente il riutilizzo del codice senza estenderlo, ma per Ereditarietà è necessario estendere la classe per qualsiasi riutilizzo di codice o funzionalità. Un'altra differenza che deriva da questo fatto è che usando la composizione è possibile riutilizzare il codice anche per la classe finale che non è estensibile ma l'ereditarietà non può riutilizzare il codice in tali casi. Inoltre, usando Composizione puoi riutilizzare il codice di molte classi in quanto sono dichiarate come solo una variabile membro, ma con Ereditarietà puoi riutilizzare il codice da una sola classe perché in Java puoi estendere solo una classe, poiché l'ereditarietà multipla non è supportata in Java . Puoi farlo in C ++ perché una classe può estendere più di una classe. A proposito, dovresti semprepreferisco la composizione all'ereditarietà in Java , non sono solo io ma anche Joshua Bloch ha suggerito nel suo libro


1
Perché dovrei "perferire la composizione sull'ereditarietà "? Sono concetti diversi e vengono utilizzati per scopi diversi. Francamente, non vedo come potresti passare dall'uno all'altro.
Kröw,

1

Penso che questo esempio spieghi chiaramente le differenze tra eredità e composizione .

In questo esempio, il problema viene risolto utilizzando l'ereditarietà e la composizione. L'autore presta attenzione al fatto che; in eredità , un cambiamento nella superclasse potrebbe causare problemi nella classe derivata, che la eredita.

Lì puoi anche vedere la differenza nella rappresentazione quando usi un UML per ereditarietà o composizione.

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


1
Link solo le risposte sono scoraggiate perché diventano stantie. Includi almeno le informazioni rilevanti più importanti dal link nella tua risposta.
Scott Solmer,

1

Eredità contro composizione.

Le eredità e la composizione sono entrambe utilizzate per la riutilizzabilità e l'estensione del comportamento di classe.

Le ereditarietà si usano principalmente in un modello di programmazione con algoritmo di famiglia come il tipo di relazione IS-A significa un tipo simile di oggetto. Esempio.

  1. Duster è un'auto
  2. Safari è un'auto

Questi appartengono alla famiglia Car.

La composizione rappresenta il tipo di relazione HAS-A. Mostra l'abilità di un oggetto come Duster ha cinque ingranaggi, Safari ha quattro ingranaggi ecc. Ogni volta che è necessario estendere l'abilità di una classe esistente, utilizzare la composizione. Esempio, dobbiamo aggiungere un altro ingranaggio nell'oggetto Duster, quindi dobbiamo creare un altro oggetto Gear e comporlo nell'oggetto Duster.

Non dovremmo apportare modifiche alla classe di base fino a / a meno che tutte le classi derivate non necessitino di tale funzionalità. Per questo scenario dovremmo usare Composizione.

classe A derivata dalla classe B

Classe A derivata dalla Classe C

Classe A derivata dalla Classe D.

Quando aggiungiamo qualsiasi funzionalità nella classe A, questa è disponibile per tutte le sottoclassi anche quando le classi C e D non richiedono tale funzionalità. Per questo scenario dobbiamo creare una classe separata per tali funzionalità e comporla nella classe richiesta ( ecco la classe B).

Di seguito è riportato l'esempio:

          // 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

Composizione significa creare un oggetto per una classe che ha relazione con quella particolare classe. Supponiamo che lo studente abbia rapporti con gli Account;

Un'eredità è, questa è la classe precedente con la funzionalità estesa. Ciò significa che questa nuova classe è la vecchia con alcune funzionalità estese. Supponiamo che Studente sia Studente ma Tutti gli Studenti sono Umani. Quindi c'è una relazione con studenti e umani. Questa è eredità.


0

No, entrambi sono diversi. La composizione segue la relazione "HAS-A" e l'eredità segue la relazione "IS-A". Il miglior esempio di composizione è stato il modello strategico.


2
La sua unica qualità di commento
Mathews Sunny,

Hai appena commentato la stessa cosa che la risposta accettata ha detto 7 anni prima di te?
Anjil Dhamala,

0

Ereditarietà significa riutilizzare la funzionalità completa di una classe, qui la mia classe deve usare tutti i metodi della superclasse e la mia classe sarà titolata accoppiata alla superclasse e il codice sarà duplicato in entrambe le classi in caso di ereditarietà.

Ma possiamo superare tutti questi problemi quando usiamo la composizione per parlare con un'altra classe. la composizione sta dichiarando un attributo di un'altra classe nella mia classe con cui vogliamo parlare. e quale funzionalità vogliamo da quella classe che possiamo ottenere usando quell'attributo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.