Différence entre l'héritage et la composition

208

La composition et l'héritage sont-ils les mêmes? Si je veux implémenter le modèle de composition, comment puis-je le faire en Java?

gmhk
la source
2
Autre question connexe: Y a-t-il quelque chose que la composition ne peut accomplir que l'héritage peut? stackoverflow.com/questions/2238642/…
ewernli
1
Voir aussi is-a-vs-has-a-which-one-is-better
nawfal
1
Cet article m'a été utile et m'a donc aidé: penséeworks.com
insights

Réponses:

321

Ils sont absolument différents. L'héritage est une relation «est-une» . La composition est un "has-a" .

Vous faites de la composition en ayant une instance d'une autre classe Ccomme champ de votre classe, au lieu de l'étendre C. Un bon exemple où la composition aurait été bien meilleure que l'héritage java.util.Stack, qui s'étend actuellement java.util.Vector. Ceci est maintenant considéré comme une erreur. Un vecteur de pile "n'est-PAS-un" ; vous ne devez pas être autorisé à insérer et à supprimer des éléments arbitrairement. Cela aurait dû être de la composition à la place.

Malheureusement, il est trop tard pour corriger cette erreur de conception, car la modification de la hiérarchie d'héritage romprait maintenant la compatibilité avec le code existant. Si la Stackcomposition avait été utilisée au lieu de l'héritage, elle peut toujours être modifiée pour utiliser une autre structure de données sans violer l'API .

Je recommande fortement le livre de Josh Bloch Effective Java 2nd Edition

  • Point 16: Privilégier la composition à l'héritage
  • Point 17: Concevoir et documenter l'héritage ou bien l'interdire

Une bonne conception orientée objet ne consiste pas à étendre généreusement les classes existantes. Votre premier instinct devrait être de composer à la place.


Voir également:

polygénelubrifiants
la source
4
Intéressant. Pourquoi ne pas simplement créer une nouvelle classe java.util.Stack2 qui utilise la composition?
qed
5
J'apprécie cette réponse; cependant, j'ai l'impression que la réponse dérape et plonge plus dans les préoccupations entourant la conception de la langue (et d'un paquet particulier) qu'elle ne répond à la question posée concernant la composition par rapport à l'héritage. Je suis un grand fan de répondre à la question sur SO, en citant les ressources - ne pas lier à des ressources externes sans fournir un résumé plus approfondi qu'un résumé d'une ligne.
Thomas
5
Mauvais exemple, j'ai dû faire des recherches supplémentaires pour comprendre ce que sont Vector et Stack.
Sam Ramezanli du
Bon mais votre exemple sur la pile java n'est pas un bon candidat en raison de cet héritage est un exemple de mauvaise décision sur le choix de l'héritage sur la composition comme dit dans cet article : oughtworks.com/insights/blog/…
QMaster
212

Composition signifie HAS A
Héritage signifieIS A

Example: La voiture a un moteur et la voiture est une automobile

Dans la programmation, cela est représenté comme:

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.
}
codaddict
la source
7
Je changerais «automobile» en «véhicule motorisé», car dans de nombreuses interprétations, voitures et automobiles sont équivalentes.
nanofarad
5
@hexafraction Je suis d'accord que le véhicule serait probablement un meilleur choix, mais en même temps -codaddict- a bien illustré le point pour ce qui a été demandé.
nckbrz
5
"un moteur":-/
Omar Tariq
bien dépensé. +1. Lectures
roottraveller
@AndreyAkhmetov Automobile peut avoir un typechamp de typeEnum
Ojonugwa Jude Ochalifu
42

Comment l'héritage peut-il être dangereux?

Prenons un exemple

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

1) Comme indiqué dans le code ci-dessus, la classe Y a un couplage très fort avec la classe X. Si quelque chose change dans la superclasse X, Y peut se briser de façon spectaculaire. Supposons qu'à l'avenir, la classe X implémente un travail de méthode avec la signature ci-dessous

public int work(){
}

Le changement se fait en classe X mais cela rendra la classe Y incompilable. Ce type de dépendance peut donc atteindre n'importe quel niveau et peut être très dangereux. Chaque fois que la superclasse peut ne pas avoir une visibilité complète pour coder dans toutes ses sous-classes et la sous-classe peut continuer à remarquer ce qui se passe dans la superclasse tout le temps. Nous devons donc éviter ce couplage fort et inutile.

Comment la composition résout-elle ce problème?

Voyons en révisant le même exemple

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

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

Ici, nous créons la référence de la classe X dans la classe Y et invoquons la méthode de la classe X en créant une instance de la classe X. Maintenant, tout ce couplage fort a disparu. La superclasse et la sous-classe sont désormais très indépendantes l'une de l'autre. Les classes peuvent librement apporter des modifications dangereuses en situation successorale.

2) Deuxième très bon avantage de la composition en ce qu'elle offre une flexibilité d'appel de méthode, par exemple:

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

public class Test{    
    R r;    
}

Dans la classe Test en utilisant la référence r, je peux invoquer des méthodes de classe X ainsi que de classe Y. Cette flexibilité n'a jamais été là dans l'héritage

3) Un autre grand avantage: les tests unitaires

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

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

Dans l'exemple ci-dessus, si l'état de l'instance x n'est pas connu, il peut facilement être simulé en utilisant certaines données de test et toutes les méthodes peuvent être facilement testées. Cela n'était pas possible du tout en héritage car vous dépendiez fortement de la superclasse pour obtenir l'état d'instance et exécuter n'importe quelle méthode.

4) Une autre bonne raison pour laquelle nous devrions éviter l'héritage est que Java ne prend pas en charge l'héritage multiple.

Prenons un exemple pour comprendre ceci:

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

Bon à savoir :

  1. la composition est facilement réalisée au moment de l'exécution tandis que l'héritage fournit ses fonctionnalités au moment de la compilation

  2. la composition est également connue sous le nom de relation HAS-A et l'héritage est également connu sous le nom de relation IS-A

Prenez donc l'habitude de toujours préférer la composition à l'héritage pour diverses raisons ci-dessus.

Manoj Kumar Saini
la source
3
d'accord mais compte tenu de votre solution en utilisant la composition .... nous avons encore besoin de faire un baby-sitting par exemple maintenant super classe X changer le nom de la méthode de faire à faire .... alors la sous-classe Y devra également être maintenue (doit être changée ainsi) c'est encore un couplage serré? Et comment s'en débarrasser?
stuckedoverflow
19

La réponse donnée par @Michael Rodrigues n'est pas correcte (je m'excuse, je ne suis pas en mesure de commenter directement), et pourrait conduire à une certaine confusion.

L'implémentation d' une interface est une forme d'héritage ... lorsque vous implémentez une interface, vous héritez non seulement de toutes les constantes, vous engagez votre objet à être du type spécifié par l'interface; c'est toujours une relation « est-un ». Si une voiture implémente Fillable , la voiture " est un " Fillable , et peut être utilisée dans votre code partout où vous utiliseriez un Fillable .

La composition est fondamentalement différente de l'héritage. Lorsque vous utilisez la composition, vous établissez (comme le notent les autres réponses) une relation " has-a " entre deux objets, par opposition à la relation " is-a " que vous établissez lorsque vous utilisez l'héritage .

Donc, à partir des exemples de voitures dans les autres questions, si je voulais dire qu'une voiture a un réservoir d'essence, j'utiliserais la composition comme suit:

public class Car {

private GasTank myCarsGasTank;

}

Espérons que cela dissipe tout malentendu.

Kris
la source
17

L'héritage fait ressortir IS-A relation . La composition fait ressortir la relation HAS-A . Le modèle de stratégie explique que la composition doit être utilisée dans les cas où il existe des familles d'algorithmes définissant un comportement particulier.
Exemple classique étant d'une classe de canard qui met en œuvre un comportement de vol.

public interface Flyable{
 public void fly();
}

public class Duck {
 Flyable fly;

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

Ainsi, nous pouvons avoir plusieurs classes qui implémentent le vol, par exemple:

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

S'il avait été question d'héritage, nous aurions deux classes d'oiseaux différentes qui implémentent la fonction mouche à maintes reprises. L'héritage et la composition sont donc complètement différents.

poulie sans friction
la source
7

La composition est exactement comme elle sonne - vous créez un objet en branchant des parties.

MODIFIER le reste de cette réponse est basé à tort sur la prémisse suivante.
Ceci est accompli avec des interfaces.
Par exemple, en utilisant l' Carexemple ci-dessus,

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

Ainsi, avec quelques composants théoriques standard, vous pouvez construire votre objet. C'est alors à vous de déterminer comment a Houseprotège ses occupants et comment a Carprotège ses occupants.

L'héritage est comme l'inverse. Vous commencez avec un objet complet (ou semi-complet) et vous remplacez ou remplacez les différents bits que vous souhaitez modifier.

Par exemple, MotorVehiclepeut venir avec une Fuelableméthode et une Driveméthode. Vous pouvez laisser la méthode Fuel telle quelle, car c'est la même chose pour remplir une moto et une voiture, mais vous pouvez remplacer la Driveméthode parce que la moto roule très différemment d'une a Car.

Avec l'héritage, certaines classes sont déjà complètement implémentées, et d'autres ont des méthodes que vous êtes obligé de remplacer. Avec Composition, rien ne vous est donné. (mais vous pouvez implémenter les interfaces en appelant des méthodes dans d'autres classes s'il vous arrive d'avoir quelque chose autour).

La composition est considérée comme plus flexible, car si vous avez une méthode telle que iUsesFuel, vous pouvez avoir une méthode ailleurs (une autre classe, un autre projet) qui se soucie simplement de traiter avec des objets pouvant être alimentés, qu'il s'agisse d'une voiture, bateau, poêle, barbecue, etc. Les interfaces exigent que les classes qui disent qu'elles implémentent cette interface aient en fait les méthodes dont il s'agit. Par exemple,

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

alors vous pouvez avoir une méthode ailleurs

private void FillHerUp(iFuelable : objectToFill) {

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

        objectToFill.AddSomeFuel();
   }

Exemple étrange, mais cela montre que cette méthode ne se soucie pas de ce qu'elle remplit, car l'objet implémente iUsesFuel, il peut être rempli. Fin de l'histoire.

Si vous utilisiez l'héritage à la place, vous auriez besoin de différentes FillHerUpméthodes pour traiter MotorVehicleset Barbecues, sauf si vous aviez un objet de base "ObjectThatUsesFuel" plutôt étrange dont vous devez hériter.

Michael Rodrigues
la source
Les conventions Java stipulent que les noms de classe et d'interface sont écrits en ThisCase, et non en camelCase. Par conséquent, il est préférable de nommer vos interfaces IDrivable, etc. Vous pourriez ne pas avoir besoin du «I» si vous regroupez correctement toutes vos interfaces dans un package.
ThePyroEagle
6

La composition et l'héritage sont-ils les mêmes?

Ce ne sont pas les mêmes.

Composition : il permet de traiter un groupe d'objets de la même manière qu'une seule instance d'un objet. L'intention d'un composite est de "composer" des objets en structures arborescentes pour représenter des hiérarchies partielles

Héritage : une classe hérite des champs et des méthodes de toutes ses superclasses, directes ou indirectes. Une sous-classe peut remplacer les méthodes dont elle hérite ou masquer les champs ou les méthodes dont elle hérite.

Si je veux implémenter le modèle de composition, comment puis-je le faire en Java?

L' article de Wikipédia est assez bon pour implémenter un modèle composite en java.

entrez la description de l'image ici

Participants clés:

Composant :

  1. Est l'abstraction pour tous les composants, y compris les composants composites
  2. Déclare l'interface des objets dans la composition

Feuille :

  1. Représente les objets feuilles dans la composition
  2. Implémente toutes les méthodes de composant

Composite :

  1. Représente un composant composite (composant ayant des enfants)
  2. Met en œuvre des méthodes pour manipuler les enfants
  3. Implémente toutes les méthodes Component, généralement en les déléguant à ses enfants

Exemple de code pour comprendre le modèle 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());
    }

}

production:

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

Explication:

  1. La partie est une feuille
  2. La voiture contient de nombreuses pièces
  3. Différentes parties de la voiture ont été ajoutées à la voiture
  4. Le prix de la voiture = somme de (prix de chaque partie )

Référez-vous à la question ci-dessous pour les avantages et les inconvénients de la composition et de l'héritage.

Vous préférez la composition à l'héritage?

Ravindra babu
la source
Est-il judicieux d'implémenter également des pièces pour la classe de voiture? Est-ce que ce ne sera pas bon si vous l'utilisez comme composition
bhalkian
4

comme autre exemple, considérons une classe de voitures, ce serait une bonne utilisation de la composition, une voiture "aurait" un moteur, une transmission, des pneus, des sièges, etc. Cela ne prolongerait aucune de ces classes.

Glace noir
la source
4

La composition est l'endroit où quelque chose est composé de parties distinctes et il a une relation forte avec ces parties. Si la partie principale décède, les autres ne peuvent pas avoir leur propre vie. Un exemple grossier est le corps humain. Retirez le cœur et toutes les autres parties disparaissent.

L'héritage est l'endroit où vous prenez simplement quelque chose qui existe déjà et que vous l'utilisez. Il n'y a pas de relation solide. Une personne peut hériter de la succession de son père, mais elle peut s'en passer.

Je ne connais pas Java donc je ne peux pas donner d'exemple mais je peux donner une explication des concepts.

Robert Rocha
la source
3

L'héritage entre deux classes, où une classe étend une autre classe établit une relation " EST A ".

La composition à l'autre extrémité contient une instance d'une autre classe dans votre classe établit une relation " A une ". La composition en java est utile car elle facilite techniquement l'héritage multiple.

John Wilson
la source
3

Dans Simple Word, l'agrégation de mots signifie a une relation.

La composition est un cas particulier d'agrégation . De manière plus spécifique, une agrégation restreinte est appelée composition. Lorsqu'un objet contient l'autre objet, si l'objet contenu ne peut pas exister sans l'existence d'un objet conteneur, il est appelé composition. Exemple: une classe contient des élèves. Un étudiant ne peut pas exister sans classe. Il existe une composition entre la classe et les étudiants.

Pourquoi utiliser l'agrégation

Réutilisation du code

Quand utiliser l'agrégation

La réutilisation du code est également mieux réalisée par agrégation lorsqu'il n'y a pas de navire de relation

Héritage

L'héritage est une relation parent-enfant signifie que l'héritage est une relation

L'héritage en java est un mécanisme dans lequel un objet acquiert toutes les propriétés et les comportements de l'objet parent.

Utilisation de l'héritage dans Java 1 Code Reusability. 2 Ajoutez une fonctionnalité supplémentaire dans la classe enfant ainsi que la substitution de méthode (afin que le polymorphisme d'exécution puisse être atteint).

Keshav Gera
la source
1

Bien que l'héritage et la composition fournissent la réutilisation du code, la principale différence entre la composition et l'héritage en Java est que la composition permet la réutilisation du code sans l'étendre, mais pour l'héritage, vous devez étendre la classe pour toute réutilisation du code ou des fonctionnalités. Une autre différence qui vient de ce fait est qu'en utilisant Composition, vous pouvez réutiliser du code même pour la classe finale qui n'est pas extensible, mais l'héritage ne peut pas réutiliser le code dans de tels cas. De plus, en utilisant Composition, vous pouvez réutiliser le code de nombreuses classes car elles sont déclarées comme une simple variable membre, mais avec l'héritage, vous pouvez réutiliser le code sous une seule classe car en Java, vous ne pouvez étendre qu'une seule classe, car l'héritage multiple n'est pas pris en charge en Java . Vous pouvez le faire en C ++ car une classe peut étendre plusieurs classes. BTW, vous devriez toujourspréfèrent la composition à l'héritage en Java , ce n'est pas seulement moi, mais même Joshua Bloch l' a suggéré dans son livre

Nitin Pawar
la source
1
Pourquoi devrais-je «préférer la composition à l'héritage »? Ce sont des concepts différents et sont utilisés à des fins différentes. Franchement, je ne vois pas comment on pourrait même passer de l'un à l'autre.
Kröw
1

Je pense que cet exemple explique clairement les différences entre l' héritage et la composition .

Dans cet exemple, le problème est résolu en utilisant l'héritage et la composition. L'auteur fait attention au fait que; dans l' héritage , un changement de superclasse peut provoquer des problèmes dans la classe dérivée, qui l'hérite.

Là, vous pouvez également voir la différence de représentation lorsque vous utilisez un UML pour l'héritage ou la composition.

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

aunte
la source
1
Les réponses de lien uniquement sont déconseillées car elles deviennent obsolètes. Veuillez inclure au moins les informations pertinentes les plus importantes du lien dans votre réponse.
Scott Solmer
1

Héritages Vs Composition.

Les héritages et la composition sont tous deux utilisés pour la réutilisation et l'extension du comportement de classe.

Les héritages principalement utilisés dans un modèle de programmation d'algorithmes familiaux tels que le type de relation IS-A signifient un type d'objet similaire. Exemple.

  1. Duster est une voiture
  2. Safari est une voiture

Ils appartiennent à la famille Car.

La composition représente le type de relation HAS-A. Elle montre la capacité d'un objet tel que Duster a cinq vitesses, Safari a quatre vitesses, etc. Chaque fois que nous devons étendre la capacité d'une classe existante, utilisez la composition. Exemple, nous devons ajouter un autre engrenage dans l'objet Duster, puis nous devons créer un autre objet engrenage et le composer dans l'objet Duster.

Nous ne devons pas apporter de modifications à la classe de base tant que / à moins que toutes les classes dérivées n'aient besoin de ces fonctionnalités. Pour ce scénario, nous devons utiliser Composition.

classe A dérivée de la classe B

Classe A dérivée de la classe C

Classe A dérivée de la classe D.

Lorsque nous ajoutons une fonctionnalité dans la classe A, elle est disponible pour toutes les sous-classes même lorsque les classes C et D ne nécessitent pas ces fonctionnalités.Pour ce scénario, nous devons créer une classe distincte pour ces fonctionnalités et la composer dans la classe requise ( voici la classe B).

Voici l'exemple:

          // 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");
                       }
                   }
Sheo Dayal Singh
la source
0

La composition signifie créer un objet à une classe qui a une relation avec cette classe particulière. Supposons que l'étudiant ait une relation avec les comptes;

Un héritage est, c'est la classe précédente avec la fonctionnalité étendue. Cela signifie que cette nouvelle classe est l'ancienne classe avec des fonctionnalités étendues. Supposons que l'étudiant soit étudiant mais que tous les étudiants soient humains. Il y a donc une relation avec l'étudiant et l'humain. C'est l'héritage.

HM Nayem
la source
0

Non, les deux sont différents. La composition suit la relation "HAS-A" et l'héritage suit la relation "IS-A". Le meilleur exemple de composition était le modèle stratégique.

Ranga Reddy
la source
2
Sa seule qualité de commentaire
Mathews Sunny
Vous venez de commenter la même chose que la réponse acceptée a dit 7 ans avant vous?
Anjil Dhamala
0

L'héritage signifie la réutilisation de la fonctionnalité complète d'une classe, ici ma classe doit utiliser toutes les méthodes de la super classe et ma classe sera couplée de manière titrée avec la super classe et le code sera dupliqué dans les deux classes en cas d'héritage.

Mais nous pouvons surmonter tous ces problèmes lorsque nous utilisons la composition pour parler avec une autre classe. composition déclare un attribut d'une autre classe dans ma classe à laquelle nous voulons parler. et quelle fonctionnalité nous voulons de cette classe, nous pouvons obtenir en utilisant cet attribut.

Vikas Kapadiya
la source