Menu
Coddy logo textTech

Patron Décorateur

Fait partie de la section Programmation Orientée Objet du Journey Java de Coddy — leçon 72 sur 87.

Le patron Décorateur est un patron de conception structurel 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.

Imaginez un café où vous commencez avec un café de base et pouvez ajouter des suppléments comme du lait, du sucre ou de la crème fouettée. Chaque ajout enveloppe le café d'origine, ajoutant son coût et sa description. Le patron utilise une interface commune afin que les objets décorés puissent être traités de la même manière que l'original :

interface Coffee {
    String getDescription();
    double getCost();
}

class SimpleCoffee implements Coffee {
    public String getDescription() {
        return "Simple Coffee";
    }
    public double getCost() {
        return 2.0;
    }
}

abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee;
    
    public CoffeeDecorator(Coffee coffee) {
        this.coffee = coffee;
    }
}

class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }
    
    public String getDescription() {
        return coffee.getDescription() + ", Milk";
    }
    
    public double getCost() {
        return coffee.getCost() + 0.5;
    }
}

Le décorateur détient une référence vers l'objet enveloppé et lui délègue les appels tout en ajoutant son propre comportement. Vous pouvez empiler plusieurs décorateurs pour combiner des fonctionnalités :

Coffee order = new SimpleCoffee();
order = new MilkDecorator(order);
order = new SugarDecorator(order);

System.out.println(order.getDescription());  // Café simple, Lait, Sucre
System.out.println(order.getCost());         // 2.75

Le pattern Decorator excelle lorsque vous devez ajouter des responsabilités à des objets sans créer une explosion de sous-classes. Chaque décorateur est indépendant, vous pouvez donc les combiner librement pour créer exactement la combinaison de comportements dont vous avez besoin.

challenge icon

Défi

Facile

Construisons un système de notification en utilisant le Decorator Pattern ! Vous allez créer une notification de base qui peut être enrichie de différents canaux de diffusion—alertes par e-mail, alertes SMS et notifications push—en enveloppant les décorateurs les uns autour des autres. Cela reflète les systèmes du monde réel où un seul message peut devoir être envoyé via plusieurs canaux simultanément.

Vous organiserez votre code sur quatre fichiers :

  • Notifier.java : Définissez l'interface Notifier qui sert d'interface de composant pour votre pattern décorateur. Elle doit déclarer une méthode send(String message) qui gère l'envoi des notifications.
  • BasicNotifier.java : Créez la classe de composant concret qui implémente Notifier. Cela représente la forme la plus simple de notification—une alerte intégrée à l'application (in-app). Lorsque send est appelée, elle doit afficher In-App: [message].
  • NotifierDecorators.java : Construisez votre hiérarchie de décorateurs ici. Commencez par une classe abstraite NotifierDecorator qui implémente Notifier et détient une référence à un objet Notifier enveloppé (passé via son constructeur).

    Ensuite, créez trois décorateurs concrets qui étendent NotifierDecorator :

    EmailDecorator - Lorsque send est appelée, elle délègue d'abord au notificateur enveloppé, puis affiche Email: [message].

    SMSDecorator - Lorsque send est appelée, elle délègue d'abord au notificateur enveloppé, puis affiche SMS: [message].

    PushDecorator - Lorsque send est appelée, elle délègue d'abord au notificateur enveloppé, puis affiche Push: [message].

  • Main.java : Rassemblez votre système de décorateurs ! Vous recevrez deux entrées : le message de notification (String) et une liste de canaux à ajouter séparés par des virgules (String), par exemple "email,sms" ou "push,email,sms".

    Commencez par un BasicNotifier. Ensuite, en fonction de l'entrée des canaux, enveloppez-le avec les décorateurs appropriés dans l'ordre où ils apparaissent. Les noms de canaux valides sont email, sms, et push.

    Enfin, appelez send avec votre message sur le notificateur entièrement décoré.

Vous recevrez deux entrées dans l'ordre : le message à envoyer (String) et les canaux à activer (String, séparés par des virgules).

Par exemple, avec les entrées Server is down! et email,push, votre sortie serait :

In-App: Server is down!
Email: Server is down!
Push: Server is down!

Remarquez comment chaque décorateur ajoute son propre comportement tout en préservant la notification d'origine. L'ordre d'enveloppement détermine l'ordre de sortie—le composant le plus interne (BasicNotifier) s'exécute en premier, puis chaque décorateur ajoute sa couche. Vous pouvez mélanger et assortir n'importe quelle combinaison de canaux sans créer de classes distinctes pour chaque combinaison !

Aide-mémoire

Le Decorator Pattern (ou patron de conception Décorateur) est un patron de conception structurel qui attache de nouveaux comportements à des objets en les enveloppant dans des objets décorateurs. Il ajoute des fonctionnalités de manière dynamique au moment de l'exécution sans modifier la classe d'origine.

Le patron utilise une interface commune afin que les objets décorés puissent être traités de la même manière que l'objet d'origine.

Structure de base

Définissez une interface de composant :

interface Coffee {
    String getDescription();
    double getCost();
}

Créez un composant concret qui implémente l'interface :

class SimpleCoffee implements Coffee {
    public String getDescription() {
        return "Simple Coffee";
    }
    public double getCost() {
        return 2.0;
    }
}

Créez une classe de décorateur abstraite qui implémente la même interface et détient une référence à l'objet enveloppé :

abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee;
    
    public CoffeeDecorator(Coffee coffee) {
        this.coffee = coffee;
    }
}

Créez des décorateurs concrets qui étendent le décorateur abstrait :

class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }
    
    public String getDescription() {
        return coffee.getDescription() + ", Milk";
    }
    
    public double getCost() {
        return coffee.getCost() + 0.5;
    }
}

Utilisation des décorateurs

Empilez plusieurs décorateurs pour combiner les fonctionnalités :

Coffee order = new SimpleCoffee();
order = new MilkDecorator(order);
order = new SugarDecorator(order);

System.out.println(order.getDescription());  // Simple Coffee, Milk, Sugar
System.out.println(order.getCost());         // 2.75

Le décorateur délègue les appels à l'objet enveloppé tout en ajoutant son propre comportement. Chaque décorateur est indépendant et peut être combiné librement.

Essayez vous-même

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Lire le message à envoyer
        String message = scanner.nextLine();
        
        // Lire la liste des canaux séparés par des virgules (par ex., "email,sms" ou "push,email,sms")
        String channelsInput = scanner.nextLine();
        
        // TODO: Commencer avec un BasicNotifier
        
        // TODO: Diviser l'entrée des canaux par la virgule
        
        // TODO: Parcourir chaque canal et envelopper le notifier avec le décorateur approprié
        // Les canaux valides sont : "email", "sms", "push"
        
        // TODO: Appeler send sur le notifier entièrement décoré avec le message
        
    }
}
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