Patron Décorateur
Fait partie de la section Programmation Orientée Objet du Journey C# de Coddy. Leçon 58 sur 70.
Le pattern Decorator est un pattern structurel qui vous permet d’ajouter de nouveaux comportements aux objets en les enveloppant dans des objets spéciaux appelés décorateurs. Contrairement à l’héritage, qui ajoute un comportement au moment de la compilation, les décorateurs ajoutent des fonctionnalités dynamiquement à l’exécution sans modifier la classe d’origine.
Le modèle fonctionne en faisant en sorte que les décorateurs implémentent la même interface que l’objet qu’ils enveloppent. Chaque décorateur conserve une référence vers 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é à s'empiler. 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 dose de lait !
Console.WriteLine(coffee.GetDescription()); // Coffee, Milk, Milk
Console.WriteLine(coffee.GetCost()); // 3.00Le patron 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 pizzas à l’aide du pattern Decorator. Vous allez créer une pizza de base, puis lui ajouter dynamiquement des garnitures, chaque garniture enveloppant la couche précédente et ajoutant des éléments à la description ainsi qu’au prix.
Vous organiserez votre code sur trois fichiers :
Pizza.cs: définissez une interfaceIPizzadans l’espace de nomsPizzaShopavec deux méthodes :GetDescription()qui renvoie une chaîne de caractères etGetPrice()qui renvoie un nombre décimal. Créez ensuite une classePlainPizzaqui implémente cette interface : elle doit renvoyer"Pizza"comme description et8.00mcomme prix de base.ToppingDecorators.cs: créez une classe abstraiteToppingDecoratorqui implémenteIPizzaet contient une référence vers la pizza enveloppée (transmise par le constructeur). Ses méthodes virtuelles doivent déléguer leur comportement à 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: assemblez 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 à ajouter, séparées par des virgules (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 doit être :
Pizza, Cheese, Pepperoni, Mushrooms
12.75Remarquez que vous pouvez ajouter plusieurs fois la même garniture ! Si l’entrée est cheese,cheese, vous obtiendrez :
Pizza, Cheese, Cheese
11.00Cela démontre la flexibilité du pattern Decorator : chaque décorateur enveloppe la pizza qu’il reçoit, qu’il s’agisse d’une pizza nature ou d’une pizza déjà décorée, ce qui vous permet d’empiler dynamiquement les comportements !
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 UtilisateurLogique du système d'empruntLINQ pour la rechercheInterface Console / Interface d'administrationTests unitaires (NUnit/xUnit)Entraînez-vous par vous-même : Compilateur C# en ligne