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.75Le 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.
Défi
FacileConstruisons 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'interfaceNotifierqui sert d'interface de composant pour votre pattern décorateur. Elle doit déclarer une méthodesend(String message)qui gère l'envoi des notifications.BasicNotifier.java: Créez la classe de composant concret qui implémenteNotifier. Cela représente la forme la plus simple de notification—une alerte intégrée à l'application (in-app). Lorsquesendest appelée, elle doit afficherIn-App: [message].NotifierDecorators.java: Construisez votre hiérarchie de décorateurs ici. Commencez par une classe abstraiteNotifierDecoratorqui implémenteNotifieret détient une référence à un objetNotifierenveloppé (passé via son constructeur).Ensuite, créez trois décorateurs concrets qui étendent
NotifierDecorator:EmailDecorator- Lorsquesendest appelée, elle délègue d'abord au notificateur enveloppé, puis afficheEmail: [message].SMSDecorator- Lorsquesendest appelée, elle délègue d'abord au notificateur enveloppé, puis afficheSMS: [message].PushDecorator- Lorsquesendest appelée, elle délègue d'abord au notificateur enveloppé, puis affichePush: [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 sontemail,sms, etpush.Enfin, appelez
sendavec 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.75Le 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
}
}
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 externesIntroduction à la POOClasses vs ObjetsLe mot-clé thisMéthodesChamps (Attributs)Méthode constructeurSurcharge de constructeurRécapitulatif - Calculatrice simple4Héritage
Héritage de base (extends)Le mot-clé superRedéfinition de méthode (@Override)Chaînage de constructeursLa classe ObjectHéritage simple et multiniveauPourquoi pas d'héritage multiple de classesRécapitulatif - Hiérarchie des employés7Méthodes spéciales et classe Object
Méthode toString()equals() et hashCode()Méthode clone()compareTo() et ComparableInterface ComparatorRécapitulatif - Tri personnalisé2Modificateurs d'accès et Encapsulation
Aperçu des niveaux d'accèsMéthodes Getter et SetterMasquage d'informationsLe mot-clé finalRécapitulatif - Gestionnaire de compte bancaire5Polymorphisme
Bases de la surcharge de méthodesRedéfinition de méthodes (Run-Time)Upcasting et DowncastingL'opérateur instanceofClasses et méthodes abstraitesRécapitulatif - Calculateur de formes8Concepts avancés de la POO
Composition vs HéritageAgrégation vs CompositionClasses internes, imbriquées et anonymesEnums et méthodes d'EnumRecords (Java 16+)Classes scellées (Java 17+)11Patrons de conception, partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FabriquePatron MonteurPatron ObservateurPatron Stratégie3Propriétés de classe et membres statiques
Variables d'instance vs variables statiquesMéthodes statiquesBlocs statiquesConstantes (static final)Récapitulatif - Compteur et utilitaire6Interfaces et Classes Abstraites
Introduction aux InterfacesImplémentation d'InterfacesImplémentation d'Interfaces MultiplesDefault et Static dans les InterfacesClasses Abstraites vs InterfacesInterfaces FonctionnellesRécapitulatif - Système de Paiement9La généricité
Introduction à la généricitéClasses génériquesMéthodes génériquesParamètres de type bornésWildcards (?, extends, super)Récapitulatif - Conteneur générique12Patrons de conception, partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Template MethodPatron ÉtatPatron CompositePatron Itérateur