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’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 lors de la compilation, les décorateurs ajoutent dynamiquement des fonctionnalités à 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é original en lui 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 conserve 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()); // Simple Coffee, Milk, Sugar
System.out.println(order.getCost()); // 2.75Le patron Decorator est particulièrement utile lorsque vous devez ajouter des responsabilités à des objets sans créer une multitude de sous-classes. Chaque décorateur est indépendant, ce qui vous permet de les combiner librement afin de créer exactement la combinaison de comportements dont vous avez besoin.
Défi
FacileConstruisons un système de notifications avec le Decorator Pattern ! Vous allez créer une notification de base qui peut être enrichie avec différents canaux de diffusion — alertes par e-mail, alertes SMS et notifications push — en imbriquant des décorateurs les uns autour des autres. Cela reflète les systèmes du monde réel dans lesquels un même message peut devoir être envoyé simultanément par plusieurs canaux.
Vous allez organiser votre code dans quatre fichiers :
Notifier.java: Define l’interfaceNotifierqui sert d’interface de composant pour votre Decorator Pattern. Elle doit déclarer une méthodesend(String message)qui gère l’envoi des notifications.BasicNotifier.java: Create la classe de composant concrete qui implementsNotifier. Elle représente la forme la plus simple d’une notification : une alerte dans l’application. Lorsquesendest appelé, elle doit afficherIn-App: [message].NotifierDecorators.java: Construisez votre hiérarchie de décorateurs ici. Commencez par une classe abstractNotifierDecoratorqui implementsNotifieret contient une référence vers un objetNotifierenveloppé (transmise par son constructor).Créez ensuite trois décorateurs concrets qui étendent
NotifierDecorator:EmailDecorator— Lorsquesendest appelé, il délègue d’abord au notificateur enveloppé, puis afficheEmail: [message].SMSDecorator— Lorsquesendest appelé, il délègue d’abord au notificateur enveloppé, puis afficheSMS: [message].PushDecorator— Lorsquesendest appelé, il délègue d’abord au notificateur enveloppé, puis affichePush: [message].Main.java: Assemblez 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 avec un
BasicNotifier. Ensuite, selon l’entrée contenant les canaux, enveloppez-le avec les décorateurs appropriés dans l’ordre où ils apparaissent. Les noms de canaux valides sontemail,smsetpush.Enfin, appelez
sendavec votre message sur le notificateur entièrement décoré.
Vous recevrez deux entrées dans l’ordre suivant : le message à envoyer (String), puis 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’imbrication détermine l’ordre de la sortie : le composant le plus interne (BasicNotifier) s’exécute en premier, puis chaque décorateur ajoute sa couche. Vous pouvez combiner librement n’importe quels canaux sans créer de classes distinctes pour chaque combinaison !
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 (p. ex., "email,sms" ou "push,email,sms")
String channelsInput = scanner.nextLine();
// TODO: Commencer avec un BasicNotifier
// TODO: Diviser l'entrée des canaux par une virgule
// TODO: Parcourir chaque canal et envelopper le notificateur avec le décorateur approprié
// Les canaux valides sont : "email", "sms", "push"
// TODO: Appeler send sur le notificateur 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érateurEntraînez-vous par vous-même : Compilateur Java en ligne