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 startedLa 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.
Défi
FacileConstruisons 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 classeProcessorqui représente un processeur. Utilisez la promotion de constructeur pour définir un$model(string) privé et un$cores(integer) privé. Ajoutez une méthodegetSpecs()qui retourne"{$this->model} ({$this->cores} cores)".Memory.php— Créez une classeMemoryqui représente la RAM. Utilisez la promotion de constructeur pour définir un$size(integer représentant les Go) privé. Ajoutez une méthodegetCapacity()qui retourne"{$this->size}GB RAM".main.php— Incluez les deux fichiers de composants et créez une classeComputerqui utilise la composition. LeComputerdoit :- Stocker un objet
Processoret un objetMemoryen 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
Computeravec ces spécifications et affichez le résultat de l'appel àdescribe().- Stocker un objet
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()
?>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
1Les bases de la POO
Fichiers externesIntroduction à la POOClasses vs ObjetsLe mot-clé $thisMéthodesPropriétésConstructeur (__construct)Destructeur (__destruct)Récapitulatif - Calculatrice simple4Héritage
Héritage de baseLe mot-clé parent::Redéfinition de méthodeLe mot-clé finalClasses abstraitesRécapitulatif - Hiérarchie des employés7Encapsulation
Public, Protégé, PrivéModificateurs d'accès en détailGetters et SettersMasquage d'informationPromotion de constructeur (8.0)Récapitulatif - Système de gestion d'étudiants10Concepts avancés de la POO
Composition vs HéritageInjection de dépendancesClasses anonymesÉnumérations (PHP 8.1)Fibers (PHP 8.1)Clonage d'objets : analyse approfondieGénérateurs et Itérateurs13Patrons de conception Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode ModèlePatron ÉtatPatron CompositePatron Repository2Espaces de noms et Autoloading
Introduction aux espaces de nomsLe mot-clé useStandard d'Autoloading PSR-4L'Autoloader ComposerRécapitulatif - Projet organisé5Interfaces et Contrats
Introduction aux InterfacesImplémentation d'InterfacesImplémentation d'Interfaces MultiplesInterface vs Classe AbstraiteType Hinting avec les InterfacesRécapitulatif - Calculateur de Formes8Méthodes magiques
Introduction aux méthodes magiques__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Clonage d'objets__serialize & __unserializeRécapitulatif - Collection personnalisée3Propriétés de classe
Propriétés d'instance vs Propriétés statiquesConstantes dans les classesMéthodes et propriétés statiquesPropriétés privées et protégéesPropriétés readonly (PHP 8.1)Récapitulatif - Gestionnaire de compte bancaire6Polymorphisme
Retour sur la redéfinition de méthodePolymorphisme via les interfacesType Hinting et Types d'unionLiaison statique tardiveRécapitulatif - Processeur de paiement9Traits
Introduction aux TraitsUtilisation de plusieurs TraitsRésolution des conflits de TraitsMéthodes abstraites dans les TraitsTraits vs Héritage12Patrons de conception Partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FabriquePatron ObservateurPatron Stratégie