Patron Décorateur
Fait partie de la section Programmation Orientée Objet du Journey C# de Coddy — leçon 58 sur 70.
Le patron Décorateur est un patron de conception structurel qui permet d'attacher de nouveaux comportements à des objets en les plaçant dans des objets spéciaux appelés décorateurs. Contrairement à l'héritage, qui ajoute du comportement au moment de la compilation, les décorateurs ajoutent des fonctionnalités de manière dynamique au moment de l'exécution sans modifier la classe d'origine.
Le patron fonctionne en faisant en sorte que les décorateurs implémentent la même interface que l'objet qu'ils enveloppent. Chaque décorateur détient une référence à l'objet enveloppé et lui délègue les appels, en ajoutant son propre comportement avant ou après :
public interface ICoffee
{
string GetDescription();
decimal GetCost();
}
public class SimpleCoffee : ICoffee
{
public string GetDescription() => "Coffee";
public decimal GetCost() => 2.00m;
}
public abstract class CoffeeDecorator : ICoffee
{
protected ICoffee _coffee;
public CoffeeDecorator(ICoffee coffee) => _coffee = coffee;
public virtual string GetDescription() => _coffee.GetDescription();
public virtual decimal GetCost() => _coffee.GetCost();
}
public class MilkDecorator : CoffeeDecorator
{
public MilkDecorator(ICoffee coffee) : base(coffee) { }
public override string GetDescription() => _coffee.GetDescription() + ", Milk";
public override decimal GetCost() => _coffee.GetCost() + 0.50m;
}La puissance des décorateurs réside dans leur capacité à se superposer. Vous pouvez envelopper un objet décoré avec un autre décorateur :
ICoffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new MilkDecorator(coffee); // Double lait !
Console.WriteLine(coffee.GetDescription()); // Café, Lait, Lait
Console.WriteLine(coffee.GetCost()); // 3.00Le patron de conception Décorateur est idéal lorsque vous devez ajouter des responsabilités à des objets sans créer une explosion de sous-classes. Chaque décorateur se concentre sur une seule préoccupation, et vous les combinez selon vos besoins pour construire le comportement exact que vous souhaitez.
Défi
FacileConstruisons un système de commande de pizza en utilisant le motif de conception Decorator (Décorateur). Vous allez créer une pizza de base, puis y ajouter dynamiquement des garnitures, chaque garniture enveloppant la couche précédente et s'ajoutant à la fois à la description et au prix.
Vous organiserez votre code sur trois fichiers :
Pizza.cs: Définissez une interfaceIPizzadans l'espace de nomsPizzaShopavec deux méthodes :GetDescription()retournant une chaîne de caractères etGetPrice()retournant un décimal. Créez ensuite une classePlainPizzaqui implémente cette interface - elle doit retourner"Pizza"comme description et8.00mcomme prix de base.ToppingDecorators.cs: Créez une classe abstraiteToppingDecoratorqui implémenteIPizzaet détient une référence à la pizza enveloppée (passée via le constructeur). Ses méthodes virtuelles doivent déléguer à la pizza enveloppée. Créez ensuite trois décorateurs concrets :CheeseTopping- ajoute", Cheese"à la description et ajoute1.50mau prixPepperoniTopping- ajoute", Pepperoni"à la description et ajoute2.00mau prixMushroomTopping- ajoute", Mushrooms"à la description et ajoute1.25mau prix
Program.cs: Rassemblez le tout en commençant par une pizza nature et en l'enveloppant de garnitures en fonction de l'entrée. Chaque décorateur de garniture enveloppe la pizza précédente, construisant la commande finale couche par couche.
Vous recevrez une entrée :
- Une liste de garnitures séparées par des virgules à ajouter (par exemple,
cheese,pepperoni,cheese)
Commencez par une PlainPizza, puis enveloppez-la avec le décorateur approprié pour chaque garniture dans l'ordre où elles apparaissent. Les garnitures valides sont cheese, pepperoni, et mushrooms. Après avoir appliqué toutes les garnitures, affichez la description sur une ligne et le prix total sur la ligne suivante.
Par exemple, si l'entrée est cheese,pepperoni,mushrooms, la sortie devrait être :
Pizza, Cheese, Pepperoni, Mushrooms
12.75Remarquez comment vous pouvez ajouter la même garniture plusieurs fois ! Si l'entrée est cheese,cheese, vous obtiendriez :
Pizza, Cheese, Cheese
11Cela démontre la flexibilité du motif décorateur - chaque décorateur enveloppe n'importe quelle pizza qu'il reçoit, qu'il s'agisse d'une pizza nature ou d'une pizza déjà décorée, vous permettant d'empiler les comportements de manière dynamique !
Aide-mémoire
Le pattern Décorateur est un patron de structure qui attache de nouveaux comportements à des objets en les enveloppant dans des objets décorateurs spéciaux. Il ajoute des fonctionnalités de manière dynamique au moment de l'exécution sans modifier la classe d'origine.
Les décorateurs implémentent la même interface que l'objet qu'ils enveloppent, détiennent une référence à l'objet enveloppé et lui délèguent les appels tout en ajoutant leur propre comportement :
public interface ICoffee
{
string GetDescription();
decimal GetCost();
}
public class SimpleCoffee : ICoffee
{
public string GetDescription() => "Coffee";
public decimal GetCost() => 2.00m;
}
public abstract class CoffeeDecorator : ICoffee
{
protected ICoffee _coffee;
public CoffeeDecorator(ICoffee coffee) => _coffee = coffee;
public virtual string GetDescription() => _coffee.GetDescription();
public virtual decimal GetCost() => _coffee.GetCost();
}
public class MilkDecorator : CoffeeDecorator
{
public MilkDecorator(ICoffee coffee) : base(coffee) { }
public override string GetDescription() => _coffee.GetDescription() + ", Milk";
public override decimal GetCost() => _coffee.GetCost() + 0.50m;
}Les décorateurs peuvent être empilés en enveloppant un objet décoré avec un autre décorateur :
ICoffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new MilkDecorator(coffee); // Double lait !
Console.WriteLine(coffee.GetDescription()); // Coffee, Milk, Milk
Console.WriteLine(coffee.GetCost()); // 3.00Utilisez le pattern Décorateur lorsque vous devez ajouter des responsabilités à des objets sans créer de nombreuses sous-classes. Chaque décorateur se concentre sur une seule préoccupation et peut être combiné selon les besoins.
Essayez vous-même
using System;
using PizzaShop;
class Program
{
public static void Main(string[] args)
{
// Lire la liste des garnitures séparées par des virgules
string input = Console.ReadLine();
string[] toppings = input.Split(',');
// TODO: Commencer avec une PlainPizza
// TODO: Parcourir chaque garniture et envelopper la pizza
// avec le décorateur approprié basé sur le nom de la garniture :
// - "cheese" -> CheeseTopping
// - "pepperoni" -> PepperoniTopping
// - "mushrooms" -> MushroomTopping
// TODO: Afficher la description finale et le prix
}
}
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
1Fondamentaux de la POO
Fichiers externesEspaces de noms et directivesIntro aux classes et objetsLe mot-clé 'this'Méthodes et paramètresChamps vs PropriétésConstructeursInitialiseurs d'objetsRécapitulatif - Calculatrice simple4Héritage
Syntaxe de base de l'héritage (:)Le mot-clé 'base'Mots-clés Virtual & OverrideClasses scelléesLa classe de base 'object'Récapitulatif - Hiérarchie des employés7Fonctionnalités avancées
Surcharge d'opérateursIndexeurs (this[])Redéfinition de ToString()Méthodes d'extensionRécapitulatif - Liste personnalisée10Patrons de conception - Partie 1
Introduction aux patrons de conceptionSingleton Thread-SafePatron FabriquePatron Observateur (Événements)Patron Stratégie2Propriétés et membres statiques
Propriétés auto-implémentéesPropriétés en lecture/écriture seuleChamps et méthodes statiquesClasses statiquesMembres à corps d'expression5Polymorphisme & Interfaces
Polymorphisme : Compilation vs ExécutionInterface vs Classe AbstraiteInterfaces MultiplesInterfaces ExplicitesUpcasting & DowncastingRécapitulatif - Calculateur de Formes8Concepts avancés de la POO
Composition plutôt qu'héritageGénériques (Classes et Méthodes)Délégués et ÉvénementsAttributs et RéflexionIDisposable et l'instruction usingBases de l'Injection de Dépendances11Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode TemplatePatron ÉtatPatron Composite3Architecture des classes
Données d'instance vs statiquesMots-clés 'readonly' & 'const'Champs de supportRécapitulatif - Gestionnaire de compte bancaire6Encapsulation
Modificateurs d'accèsPropriétés pour l'encapsulationImplémentation du masquage de donnéesPatterns d'immuabilitéRécapitulatif - Dossiers d'étudiants12Projet : Système de gestion de bibliothèque
Structure du projetModèles Livre et Utilisateur