Patron Décorateur
Fait partie de la section Programmation Orientée Objet du Journey PHP de Coddy — leçon 77 sur 91.
Le Decorator Pattern est un patron de conception structurel qui vous permet d'ajouter dynamiquement de nouveaux comportements à des objets en les enveloppant dans des objets wrappers spéciaux appelés décorateurs. Contrairement à l'héritage, qui ajoute du comportement au moment de la compilation, les décorateurs ajoutent du comportement au moment de l'exécution.
Imaginez un café. Vous commencez par un café de base, puis vous ajoutez des suppléments comme du lait, du sucre ou de la crème fouettée.
Chaque ajout enveloppe le café d'origine et s'ajoute à sa description et à son coût. L'idée clé est que le café de base et les décorateurs partagent la même interface.
<?php
interface Coffee {
public function getDescription(): string;
public function getCost(): float;
}
class SimpleCoffee implements Coffee {
public function getDescription(): string {
return "Simple Coffee";
}
public function getCost(): float {
return 2.00;
}
}
Les décorateurs implémentent la même interface et enveloppent un autre objet Coffee :
<?php
class MilkDecorator implements Coffee {
public function __construct(private Coffee $coffee) {}
public function getDescription(): string {
return $this->coffee->getDescription() . ", Milk";
}
public function getCost(): float {
return $this->coffee->getCost() + 0.50;
}
}
class SugarDecorator implements Coffee {
public function __construct(private Coffee $coffee) {}
public function getDescription(): string {
return $this->coffee->getDescription() . ", Sugar";
}
public function getCost(): float {
return $this->coffee->getCost() + 0.25;
}
}
$coffee = new SimpleCoffee();
$coffee = new MilkDecorator($coffee);
$coffee = new SugarDecorator($coffee);
echo $coffee->getDescription() . " = $" . $coffee->getCost();
Sortie :
Simple Coffee, Milk, Sugar = $2.75Chaque décorateur enveloppe l'objet précédent, formant une chaîne. Le patron permet de combiner des comportements dans n'importe quel ordre et quantité sans créer une classe pour chaque combinaison possible. Besoin d'une double dose de lait ? Enveloppez-le simplement deux fois.
Défi
FacileConstruisons un système de commande de pizza en utilisant le Patron de Conception Décorateur (Decorator Pattern). Vous commencerez par une pizza de base et ajouterez dynamiquement des garnitures — chaque garniture enveloppe la pizza et s'ajoute à sa description et à son prix, tout comme l'exemple du café de la leçon.
Vous organiserez votre code sur quatre fichiers :
Pizza.php— Définissez une interfacePizzaqui établit le contrat pour toutes les pizzas et garnitures. Elle doit avoir deux méthodes :getDescription(): stringetgetPrice(): float.BasePizza.php— Incluez l'interface Pizza et créez une classeMargheritaPizzaqui l'implémente. C'est votre pizza de base avec une description de"Margherita"et un prix de8.00.Toppings.php— Incluez l'interface Pizza et créez trois classes de décorateurs qui l'implémentent. Chaque décorateur enveloppe un objetPizza(acceptez-le dans le constructeur) et améliore à la fois la description et le prix :CheeseTopping— Ajoute", Extra Cheese"à la description et1.50au prixPepperoniTopping— Ajoute", Pepperoni"à la description et2.00au prixMushroomTopping— Ajoute", Mushrooms"à la description et1.25au prix
main.php— Incluez les fichiers BasePizza et Toppings. Vous recevrez une entrée : une liste de garnitures séparées par des virgules à ajouter (par exemple,"cheese,pepperoni"ou"mushroom,cheese,cheese").Commencez par une
MargheritaPizza. Pour chaque garniture dans l'entrée, enveloppez votre pizza avec le décorateur approprié :"cheese"enveloppe avecCheeseTopping"pepperoni"enveloppe avecPepperoniTopping"mushroom"enveloppe avecMushroomTopping
Après avoir appliqué toutes les garnitures, affichez la description et le prix de la pizza finale dans ce format :
[description] = $[price]Formatez le prix avec deux décimales.
Remarquez comment la même garniture peut être appliquée plusieurs fois — commander un double fromage signifie simplement envelopper avec CheeseTopping deux fois. Cette flexibilité est la force du Patron de Conception Décorateur !
Aide-mémoire
Le Pattern Décorateur est un patron de conception structurel qui ajoute dynamiquement des comportements aux objets en les enveloppant dans des objets décorateurs au moment de l'exécution.
L'objet de base et les décorateurs implémentent tous deux la même interface :
<?php
interface Coffee {
public function getDescription(): string;
public function getCost(): float;
}
class SimpleCoffee implements Coffee {
public function getDescription(): string {
return "Simple Coffee";
}
public function getCost(): float {
return 2.00;
}
}
Les décorateurs enveloppent un autre objet du même type d'interface :
<?php
class MilkDecorator implements Coffee {
public function __construct(private Coffee $coffee) {}
public function getDescription(): string {
return $this->coffee->getDescription() . ", Milk";
}
public function getCost(): float {
return $this->coffee->getCost() + 0.50;
}
}
class SugarDecorator implements Coffee {
public function __construct(private Coffee $coffee) {}
public function getDescription(): string {
return $this->coffee->getDescription() . ", Sugar";
}
public function getCost(): float {
return $this->coffee->getCost() + 0.25;
}
}
Enchaînez les décorateurs pour combiner les comportements :
$coffee = new SimpleCoffee();
$coffee = new MilkDecorator($coffee);
$coffee = new SugarDecorator($coffee);
echo $coffee->getDescription() . " = $" . $coffee->getCost();
// Sortie : Simple Coffee, Milk, Sugar = $2.75
Les décorateurs peuvent être appliqués plusieurs fois et dans n'importe quel ordre sans avoir à créer de classes pour chaque combinaison.
Essayez vous-même
<?php
require_once 'BasePizza.php';
require_once 'Toppings.php';
// Lire l'entrée - liste de garnitures séparées par des virgules
$input = trim(fgets(STDIN));
$toppings = explode(',', $input);
// Commencer avec une MargheritaPizza de base
$pizza = new MargheritaPizza();
// TODO: Parcourir chaque garniture et envelopper la pizza avec le décorateur approprié
// - "cheese" -> envelopper avec CheeseTopping
// - "pepperoni" -> envelopper avec PepperoniTopping
// - "mushroom" -> envelopper avec MushroomTopping
// TODO: Afficher le résultat au format : [description] = $[price]
// Formater le prix à 2 décimales en utilisant number_format()
?>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