Composition vs Héritage

La composition vs l’héritage est l’une des questions d’entretien les plus fréquemment posées. Vous avez sûrement aussi entendu dire d’utiliser la composition plutôt que l’héritage.

Composition vs Héritage

La composition et l’héritage sont tous deux des concepts de programmation orientée objet. Ils ne sont pas liés à un langage de programmation spécifique comme Java. Avant de comparer la composition à l’héritage de manière programmatique, définissons-les rapidement.

Composition

La composition est une technique de conception en programmation orientée objet qui permet de mettre en œuvre une relation « a-un » entre les objets. La composition en Java est réalisée en utilisant des variables d’instance d’autres objets. Par exemple, une personne qui a un emploi est implémentée comme suit en programmation orientée objet en Java.

package com.journaldev.composition;

public class Job {
// variables, méthodes, etc.
}
package com.journaldev.composition;

public class Person {

    // relation de composition "a-un"
    private Job job;

    // variables, méthodes, constructeurs, etc. orienté objet

Héritage

L’héritage est la technique de conception en programmation orientée objet pour implémenter la relation est-un entre les objets. L’héritage en Java est implémenté à l’aide du mot-clé extends. Par exemple, la relation Cat est un Animal en programmation Java sera implémentée comme suit.

package com.journaldev.inheritance;
 
public class Animal {
// variables, méthodes etc.
}
package com.journaldev.inheritance;
 
public class Cat extends Animal{
}

Composition over Inheritance

À la fois la composition et l’héritage favorisent la réutilisation du code à travers différentes approches. Alors lequel choisir? Comment comparer la composition et l’héritage. Vous avez dû entendre qu’en programmation, vous devriez privilégier la composition plutôt que l’héritage. Voyons quelques-unes des raisons qui vous aideront à choisir entre la composition et l’héritage.

  1. L’héritage est étroitement couplé alors que la composition est peu couplée. Supposons que nous avons les classes suivantes avec héritage.

    package com.journaldev.java.examples;
    
    public class ClassA {
    
    	public void foo(){	
    	}
    }
    
    class ClassB extends ClassA{
    	public void bar(){
    		
    	}
    }
    

    Pour simplifier, nous avons à la fois la superclasse et la sous-classe dans un seul package. Mais le plus souvent, elles seront dans des bases de code séparées. Il peut y avoir de nombreuses classes étendant la superclasse ClassA. Un exemple très courant de cette situation est l’extension de la classe Exception. Maintenant, supposons que l’implémentation de ClassA soit modifiée comme suit, une nouvelle méthode bar() est ajoutée.

    package com.journaldev.java.examples;
    
    public class ClassA {
    
    	public void foo(){	
    	}
    	
    	public int bar(){
    		return 0;
    	}
    }
    

    Dès que vous commencez à utiliser la nouvelle implémentation de ClassA, vous obtiendrez une erreur de compilation dans ClassB indiquant Le type de retour est incompatible avec ClassA.bar(). La solution serait de modifier soit la superclasse, soit la méthode bar() de la sous-classe pour les rendre compatibles. Si vous aviez utilisé la composition plutôt que l’héritage, vous n’auriez jamais rencontré ce problème. Un exemple simple d’implémentation de ClassB utilisant la composition pourrait être le suivant.

    class ClassB{
    	ClassA classA = new ClassA();
    	
    	public void bar(){
    		classA.foo();
    		classA.bar();
    	}
    }
    
  2. Il n’y a pas de contrôle d’accès dans l’héritage, alors que l’accès peut être restreint dans la composition. Nous exposons toutes les méthodes de la superclasse aux autres classes ayant accès à la sous-classe. Ainsi, si une nouvelle méthode est introduite ou s’il y a des failles de sécurité dans la superclasse, la sous-classe devient vulnérable. Comme dans la composition, nous choisissons quelles méthodes utiliser, elle est plus sécurisée que l’héritage. Par exemple, nous pouvons fournir l’exposition de la méthode foo() de ClassA à d’autres classes en utilisant le code suivant dans ClassB.

    class ClassB {
    	
    	ClassA classA = new ClassA();
    	
    	public void foo(){
    		classA.foo();
    	}
    	
    	public void bar(){	
    	}
    	
    }
    

    C’est l’un des principaux avantages de la composition par rapport à l’héritage.

  3. La composition offre une flexibilité dans l’invocation des méthodes qui est utile dans un scénario avec plusieurs sous-classes. Par exemple, supposons que nous ayons le scénario d’héritage ci-dessous.

    abstract class Abs {
        abstract void foo();
    }
    
    public class ClassA extends Abs {
    
        public void foo(){    
        }
        
    }
    
    class ClassB extends Abs{
            
        public void foo(){
        }
        
    }
    
    class Test {
        
        ClassA a = new ClassA();
        ClassB b = new ClassB();
    
        public void test(){
            a.foo();
            b.foo();
        }
    }
    

    Alors, que se passe-t-il s’il y a plus de sous-classes, est-ce que la composition rendra notre code laid en ayant une instance pour chaque sous-classe ? Non, nous pouvons réécrire la classe Test comme ci-dessous.

    class Test {
        Abs obj = null;
        
        Test1(Abs o){
            this.obj = o;
        }
        
        public void foo(){
            this.obj.foo();
        }
    
    }
    

    Cela vous donnera la flexibilité d’utiliser n’importe quelle sous-classe en fonction de l’objet utilisé dans le constructeur.

  4. Un autre avantage de la composition par rapport à l’héritage est la portée des tests. Les tests unitaires sont faciles en composition car nous savons quelles méthodes nous utilisons d’une autre classe. Nous pouvons le simuler pour les tests alors qu’en héritage nous dépendons fortement de la superclasse et ne savons pas quelles méthodes de la superclasse seront utilisées. Nous devrons donc tester toutes les méthodes de la superclasse. C’est un travail supplémentaire et nous devons le faire inutilement à cause de l’héritage.

C’est tout pour la composition contre l’héritage. Vous avez suffisamment de raisons de choisir la composition plutôt que l’héritage. Utilisez l’héritage seulement lorsque vous êtes sûr que la superclasse ne sera pas modifiée, sinon optez pour la composition.

Source:
https://www.digitalocean.com/community/tutorials/composition-vs-inheritance