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 patron Décorateur (Decorator pattern) est un patron de structure qui vous permet d'attacher de nouveaux comportements à des objets en les plaçant à l'intérieur d'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 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 pizza en utilisant le motif Decorator. 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 interface IPizza dans l'espace de noms PizzaShop avec deux méthodes : GetDescription() retournant une chaîne de caractères et GetPrice() retournant un décimal. Créez ensuite une classe PlainPizza qui implémente cette interface - elle doit retourner "Pizza" comme description et 8.00m comme prix de base.
  • ToppingDecorators.cs : Créez une classe abstraite ToppingDecorator qui implémente IPizza et 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 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 : Rassemblez le tout en commençant par une pizza nature et en l'enveloppant avec des garnitures basées sur 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.75

Remarquez comment vous pouvez ajouter la même garniture plusieurs fois ! Si l'entrée est cheese,cheese, vous obtiendriez :

Pizza, Cheese, Cheese
11.00

Cela 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 dynamiquement !

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