Menu
Coddy logo textTech

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.00

Le 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.

challenge icon

Défi

Facile

Construisons 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 interface IPizza dans l’espace de noms PizzaShop avec deux méthodes : GetDescription() qui renvoie une chaîne de caractères et GetPrice() qui renvoie un nombre décimal. Créez ensuite une classe PlainPizza qui implémente cette interface : elle doit renvoyer "Pizza" comme description et 8.00m comme prix de base.
  • ToppingDecorators.cs : créez une classe abstraite ToppingDecorator qui implémente IPizza et 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 ajoute 1.50m au prix
    • PepperoniTopping : ajoute ", Pepperoni" à la description et ajoute 2.00m au prix
    • MushroomTopping : ajoute ", Mushrooms" à la description et ajoute 1.25m au 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.75

Remarquez que vous pouvez ajouter plusieurs fois la même garniture ! Si l’entrée est cheese,cheese, vous obtiendrez :

Pizza, Cheese, Cheese
11.00

Cela 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
    }
}
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

Entraînez-vous par vous-même : Compilateur C# en ligne