Menu
Coddy logo textTech

Composition vs Héritage

Fait partie de la section Programmation Orientée Objet du Journey PHP de Coddy — leçon 56 sur 91.

Dans le chapitre précédent, vous avez appris que les traits fournissent une capacité « a un » (has-a) tandis que l'héritage établit une relation « est un » (is-a). La composition pousse le concept « a un » plus loin en construisant des objets complexes à partir d'objets plus simples, en les stockant sous forme de propriétés plutôt qu'en héritant d'eux.

Avec l'héritage, une Car pourrait étendre Vehicle. Avec la composition, une Car contient un objet Engine :

<?php
class Engine {
    public function start(): string {
        return "Engine started";
    }
}

class Car {
    private Engine $engine;
    
    public function __construct() {
        $this->engine = new Engine();
    }
    
    public function start(): string {
        return $this->engine->start();
    }
}

$car = new Car();
echo $car->start();

Sortie :

Engine started

La classe Car n'hérite pas de Engine - elle en possède une. Cette approche offre des avantages significatifs : vous pouvez remplacer le moteur par une implémentation différente, les classes restent faiblement couplées, et vous évitez les hiérarchies rigides que crée l'héritage profond.

Un principe courant en POO est « privilégier la composition plutôt que l'héritage ». L'héritage fonctionne bien pour les véritables hiérarchies de types (un Dog est véritablement un Animal), mais la composition excelle lorsque vous assemblez des fonctionnalités à partir de parties indépendantes. Vous pouvez combiner plusieurs objets, les remplacer au moment de l'exécution et tester les composants de manière isolée.

La leçon suivante explore l'injection de dépendances, qui s'appuie directement sur la composition en contrôlant la manière dont les objets reçoivent leurs dépendances.

challenge icon

Défi

Facile

Construisons un système informatique en utilisant la composition, où un Computer est assemblé à partir d'objets composants séparés plutôt que d'en hériter. Cela démontre comment la composition vous permet de construire des objets complexes à partir de parties plus simples et indépendantes.

Vous organiserez votre code sur trois fichiers :

  • Processor.php — Créez une classe Processor qui représente un processeur. Utilisez la promotion de constructeur pour définir un $model (string) privé et un $cores (integer) privé. Ajoutez une méthode getSpecs() qui retourne "{$this->model} ({$this->cores} cores)".
  • Memory.php — Créez une classe Memory qui représente la RAM. Utilisez la promotion de constructeur pour définir un $size (integer représentant les Go) privé. Ajoutez une méthode getCapacity() qui retourne "{$this->size}GB RAM".
  • main.php — Incluez les deux fichiers de composants et créez une classe Computer qui utilise la composition. Le Computer doit :
    • Stocker un objet Processor et un objet Memory en tant que propriétés privées
    • Accepter un modèle de processeur, un nombre de cœurs et une taille de mémoire dans son constructeur
    • Créer les objets composants en interne lors de la construction
    • Avoir une méthode describe() qui retourne "Computer with " . $this->processor->getSpecs() . " and " . $this->memory->getCapacity()

    Vous recevrez trois entrées : un modèle de processeur, un nombre de cœurs et une taille de mémoire en Go. Créez un Computer avec ces spécifications et affichez le résultat de l'appel à describe().

Remarquez comment le Computer n'étend pas Processor ou Memory — il les contient. Cette relation de type "a-un" (has-a) via la composition maintient chaque composant indépendant et réutilisable, tout en permettant au Computer de déléguer à ses parties en cas de besoin.

Aide-mémoire

La composition construit des objets complexes à partir d'objets plus simples en les stockant en tant que propriétés plutôt qu'en héritant d'eux. Cela crée une relation de type "a un" (has-a) où une classe contient des instances d'autres classes.

Exemple de composition de base :

<?php
class Engine {
    public function start(): string {
        return "Engine started";
    }
}

class Car {
    private Engine $engine;
    
    public function __construct() {
        $this->engine = new Engine();
    }
    
    public function start(): string {
        return $this->engine->start();
    }
}

La classe Car n'hérite pas de Engine - elle en possède une. Cela permet de garder les classes faiblement couplées et d'éviter les hiérarchies d'héritage rigides.

Privilégiez la composition à l'héritage : L'héritage fonctionne bien pour les véritables hiérarchies de types (un Dog est un Animal), mais la composition excelle lors de l'assemblage de fonctionnalités à partir de parties indépendantes. Vous pouvez combiner plusieurs objets, les remplacer au moment de l'exécution et tester les composants de manière isolée.

Essayez vous-même

<?php
require_once 'Processor.php';
require_once 'Memory.php';

class Computer
{
    // TODO : Définir des propriétés privées pour stocker les objets Processor et Memory

    // TODO : Le constructeur doit accepter le modèle du processeur, le nombre de cœurs et la taille de la mémoire
    // et créer les objets composants en interne
    public function __construct()
    {
        // TODO : Créer les objets Processor et Memory et les stocker
    }

    // TODO : Implémenter la méthode describe() qui retourne
    // "Computer with [processor specs] and [memory capacity]"
    public function describe(): string
    {
        // TODO : Déléguer aux objets composants et retourner la chaîne formatée
    }
}

// Lire l'entrée
$processorModel = trim(fgets(STDIN));
$cores = intval(fgets(STDIN));
$memorySize = intval(fgets(STDIN));

// TODO : Créer un Computer avec les spécifications données et afficher le résultat de describe()

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