Menu
Coddy logo textTech

Composition vs Héritage

Fait partie de la section Programmation Orientée Objet du Journey Java de Coddy. Leçon 47 sur 87.

L’Inheritance crée une relation « est-un » : un Dog est un Animal. Mais que se passe-t-il si une classe a besoin de fonctionnalités provenant de quelque chose qu’elle n’est pas ? C’est là qu’intervient la composition, qui établit plutôt une relation « possède-un ».

Avec la composition, une classe contient des instances d’autres classes en tant que champs plutôt que d’en hériter :

// Approche par héritage : Car EST un Engine (n'a pas de sens)
class Car extends Engine { }

// Approche par composition : Car A un Engine (a du sens)
class Car {
    private Engine engine;
    
    public Car(Engine engine) {
        this.engine = engine;
    }
    
    public void start() {
        engine.ignite();
    }
}

La composition offre plusieurs avantages. Vous pouvez modifier l’objet composé à l’exécution, par exemple en échangeant les moteurs.

Vous pouvez également combiner les comportements de plusieurs classes, ce que l’héritage ne permet pas en Java. La classe composée n’expose que les méthodes qu’elle choisit, ce qui assure un meilleur encapsulage.

Une règle pratique : utilise l’héritage lorsqu’il existe une véritable relation « est-un » et que la sous-classe représente réellement une version spécialisée de la classe parente.

Utilisez la composition lorsque vous devez utiliser les fonctionnalités d’une autre classe sans être de ce type. De nombreux développeurs expérimentés suivent le principe suivant : "favoriser la composition plutôt que l’héritage", car cela permet d’obtenir un code plus flexible et plus facile à maintenir.

challenge icon

Défi

Facile

Construisons un système informatique en utilisant la composition pour démontrer la relation « has-a ». Au lieu qu'un Computer hérite de ses composants (ce qui n'aurait pas de sens), votre ordinateur contiendra les éléments dont il a besoin pour fonctionner.

Vous organiserez votre code sur trois fichiers :

  • Processor.java : créez une classe représentant un CPU. Un Processor possède deux champs privés : brand (String) et speedGHz (double). Ajoutez un constructeur pour initialiser les deux champs ainsi que des méthodes getter pour chacun d'eux. Ajoutez une méthode process() qui renvoie une String : [brand] processing at [speedGHz] GHz
  • Memory.java : créez une classe représentant la RAM. Une Memory possède deux champs privés : type (String, comme "DDR4" ou "DDR5") et sizeGB (int). Ajoutez un constructeur, des getters et une méthode load() qui renvoie : Loading [sizeGB]GB [type] memory
  • Computer.java : c'est ici que la composition révèle toute sa puissance ! Votre classe Computer doit posséder un Processor et une Memory en tant que champs privés. Elle n'en hérite pas : elle les contient. Ajoutez un constructeur qui accepte les deux composants. Ajoutez une méthode boot() qui renvoie une String multiligne montrant le démarrage de l'ordinateur en utilisant ses composants :
    Booting computer...
    [processor.process() result]
    [memory.load() result]
    System ready!
    Ajoutez également une méthode getSpecs() qui renvoie : Specs: [processor brand] CPU, [memory sizeGB]GB [memory type]
  • Main.java : donnez vie à votre système composé ! Vous recevrez quatre entrées : la marque du processeur (String), la vitesse du processeur (double), le type de mémoire (String) et la taille de la mémoire (int).

    Créez un Processor et une Memory avec ces valeurs, puis composez-les dans un Computer. Affichez le résultat de l'appel à boot(), puis affichez le résultat de getSpecs().

Vous recevrez quatre entrées dans l'ordre suivant : la marque du processeur, la vitesse du processeur (GHz), le type de mémoire et la taille de la mémoire (GB).

Remarquez comment le Computer délègue le travail à ses composants au lieu d'essayer d'être eux. C'est toute la puissance de la composition ! Le Computer peut utiliser n'importe quel Processor ou Memory que vous lui fournissez, ce qui rend la conception flexible et réaliste.

Essayez vous-même

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Lire les entrées
        String processorBrand = scanner.nextLine();
        double processorSpeed = Double.parseDouble(scanner.nextLine());
        String memoryType = scanner.nextLine();
        int memorySize = Integer.parseInt(scanner.nextLine());
        
        // TODO: Créer un Processor avec la marque et la vitesse
        
        // TODO: Créer une Memory avec le type et la taille
        
        // TODO: Créer un Computer en composant le Processor et la Memory
        
        // TODO: Afficher le résultat de boot()
        
        // TODO: Afficher le résultat de getSpecs()
    }
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation Orientée Objet

Entraînez-vous par vous-même : Compilateur Java en ligne