Menu
Coddy logo textTech

Composition vs Héritage

Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy — leçon 76 sur 104.

Lors de la conception de relations entre les classes, vous avez deux options principales : l'héritage (« est-un ») et la composition (« a-un »). Choisir la bonne approche a un impact significatif sur la flexibilité et la maintenabilité de votre code.

L'héritage crée un couplage fort entre les classes. Une Car qui hérite de Vehicle est liée de manière permanente à cette relation :

class Vehicle {
public:
    virtual void start() { std::cout << "Starting vehicle\n"; }
};

class Car : public Vehicle {
public:
    void start() override { std::cout << "Starting car\n"; }
};

La composition intègre des objets en tant que membres, créant une relation plus flexible. Une Car a un Engine plutôt que d'être un moteur :

class Engine {
public:
    void start() { std::cout << "Engine running\n"; }
};

class Car {
private:
    Engine engine;  // Car A un Engine
public:
    void start() { engine.start(); }
};

L'avantage clé de la composition est la flexibilité. Vous pouvez facilement échanger des composants, modifier le comportement au moment de l'exécution ou combiner plusieurs capacités sans les contraintes d'une hiérarchie de classes rigide. La conception moderne en C++ privilégie la composition par rapport à l'héritage dans la plupart des cas.

Utilisez l'héritage lorsqu'il existe une véritable relation « est un » (is-a) et que vous avez besoin de polymorphisme. Utilisez la composition lorsque vous souhaitez réutiliser des fonctionnalités ou lorsque la relation est mieux décrite par « a un » (has-a) ou « utilise un » (uses-a).

challenge icon

Défi

Facile

Construisons un simulateur de système informatique qui démontre quand utiliser la composition par rapport à l'héritage. Vous allez modéliser un ordinateur qui possède des composants (composition) plutôt que d'être un composant, montrant comment la composition offre la flexibilité de changer des pièces au moment de l'exécution.

Vous organiserez votre code sur trois fichiers :

  • Components.h : Définissez les composants individuels qu'un ordinateur peut contenir.

    Créez une classe CPU avec un std::string model et un int cores privés. Fournissez un constructeur qui prend les deux valeurs, une méthode process() qui affiche CPU [model] processing with [cores] cores, et une méthode getModel().

    Créez une classe Memory avec un int sizeGB privé. Fournissez un constructeur, une méthode load() qui affiche Loading data into [sizeGB]GB RAM, et une méthode getSize().

    Créez une classe Storage avec un std::string type privé (comme "SSD" ou "HDD") et un int capacityGB. Fournissez un constructeur, une méthode read() qui affiche Reading from [capacityGB]GB [type], et des méthodes getter pour les deux champs.

  • Computer.h : Définissez une classe Computer qui utilise la composition pour combiner les composants.

    Votre Computer doit avoir des objets membres privés : un CPU, une Memory, et un Storage. Utilisez la liste d'initialisation du constructeur pour initialiser ces composants à partir des paramètres.

    Implémentez ces méthodes :

    • boot() — affiche Booting computer..., puis appelle load() sur la mémoire, read() sur le stockage, et process() sur le processeur, chacun sur une ligne distincte
    • specs() — affiche les spécifications de l'ordinateur dans ce format :
      Computer Specs:
      - CPU: [model] ([cores] cores)
      - RAM: [size]GB
      - Storage: [capacity]GB [type]
  • main.cpp : Lisez six entrées (chacune sur une ligne distincte) :
    1. Nom du modèle de CPU (chaîne de caractères)
    2. Nombre de cœurs de CPU (entier)
    3. Taille de la mémoire en Go (entier)
    4. Type de stockage (chaîne de caractères, par exemple, "SSD")
    5. Capacité de stockage en Go (entier)
    6. Une commande : soit "boot" ou "specs"

    Créez les objets composants, puis créez un Computer en utilisant la composition. Selon la commande, appelez soit boot() soit specs().

Par exemple, avec les entrées Intel i7, 8, 16, SSD, 512, et boot :

Booting computer...
Loading data into 16GB RAM
Reading from 512GB SSD
CPU Intel i7 processing with 8 cores

Avec les entrées AMD Ryzen, 6, 32, HDD, 1000, et specs :

Computer Specs:
- CPU: AMD Ryzen (6 cores)
- RAM: 32GB
- Storage: 1000GB HDD

Remarquez comment la classe Computer n'hérite d'aucun composant — elle les contient. Cette relation de type "a-un" signifie que vous pourriez facilement créer des ordinateurs avec différentes combinaisons de composants, ou même échanger des composants plus tard si vous ajoutiez des méthodes setter. Cette flexibilité est l'avantage clé de la composition par rapport à l'héritage.

Aide-mémoire

Lors de la conception de relations entre classes, choisissez entre l'héritage (« est un ») et la composition (« a un »).

L'héritage crée un couplage fort entre les classes :

class Vehicle {
public:
    virtual void start() { std::cout << "Starting vehicle\n"; }
};

class Car : public Vehicle {
public:
    void start() override { std::cout << "Starting car\n"; }
};

La composition intègre des objets en tant que membres, créant ainsi des relations flexibles :

class Engine {
public:
    void start() { std::cout << "Engine running\n"; }
};

class Car {
private:
    Engine engine;  // Car A un Engine
public:
    void start() { engine.start(); }
};

Quand utiliser l'un ou l'autre :

  • Utilisez l'héritage pour les véritables relations « est un » lorsque vous avez besoin de polymorphisme
  • Utilisez la composition pour réutiliser des fonctionnalités ou pour des relations « a un »/« utilise un »
  • La conception moderne en C++ privilégie la composition pour sa flexibilité — vous pouvez échanger des composants, changer le comportement à l'exécution et combiner des capacités sans hiérarchies rigides

Essayez vous-même

#include <iostream>
#include <string>
#include "Computer.h"

using namespace std;

int main() {
    // Lire les entrées
    string cpuModel;
    getline(cin, cpuModel);  // Nom du modèle de CPU (peut contenir des espaces)
    
    int cpuCores;
    cin >> cpuCores;  // Nombre de cœurs du CPU
    
    int memorySize;
    cin >> memorySize;  // Taille de la mémoire en GB
    
    string storageType;
    cin >> storageType;  // Type de stockage (ex: "SSD")
    
    int storageCapacity;
    cin >> storageCapacity;  // Capacité de stockage en GB
    
    string command;
    cin >> command;  // Commande : "boot" ou "specs"

    // TODO: Créer les objets composants (CPU, Memory, Storage)
    
    // TODO: Créer un objet Computer en utilisant la composition
    
    // TODO: Selon la commande, appeler soit boot() soit specs()

    return 0;
}
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