Patron Décorateur
Fait partie de la section Programmation Orientée Objet du Journey Dart de Coddy — leçon 96 sur 110.
Le patron Décorateur vous permet d'ajouter dynamiquement de nouveaux comportements à des objets en les enveloppant dans des objets spéciaux appelés décorateurs. Contrairement à l'héritage, qui ajoute du comportement au moment de la compilation, les décorateurs attachent des responsabilités supplémentaires au moment de l'exécution sans modifier la classe d'origine.
L'idée clé est que l'objet original et les décorateurs implémentent tous deux la même interface.
Un décorateur enveloppe l'objet original, lui délègue des tâches et ajoute son propre comportement avant ou après :
abstract class Coffee {
String getDescription();
double getCost();
}
class SimpleCoffee implements Coffee {
@override
String getDescription() => 'Simple Coffee';
@override
double getCost() => 2.0;
}
class MilkDecorator implements Coffee {
final Coffee _coffee;
MilkDecorator(this._coffee);
@override
String getDescription() => '${_coffee.getDescription()}, Milk';
@override
double getCost() => _coffee.getCost() + 0.5;
}
class SugarDecorator implements Coffee {
final Coffee _coffee;
SugarDecorator(this._coffee);
@override
String getDescription() => '${_coffee.getDescription()}, Sugar';
@override
double getCost() => _coffee.getCost() + 0.2;
}
void main() {
Coffee coffee = SimpleCoffee();
coffee = MilkDecorator(coffee);
coffee = SugarDecorator(coffee);
print(coffee.getDescription()); // Simple Coffee, Milk, Sugar
print(coffee.getCost()); // 2.7
}Chaque décorateur enveloppe l'objet précédent et l'améliore. Vous pouvez empiler plusieurs décorateurs dans n'importe quelle combinaison, créant ainsi un comportement flexible sans avoir à créer une sous-classe pour chaque combinaison possible. Ce patron est idéal lorsque vous devez ajouter des fonctionnalités à des objets sans affecter les autres objets de la même classe.
Défi
FacileConstruisons un système de commande de pizza en utilisant le motif Decorator ! Vous allez créer une pizza de base qui peut être enrichie de diverses garnitures, où chaque garniture enveloppe l'élément précédent et s'ajoute à la fois à la description et au prix.
Vous organiserez votre code en deux fichiers :
pizza.dart: Ce fichier contient votre interface pizza et toutes les classes de décorateurs. Commencez par une classe abstraitePizzaqui définit deux méthodes :String getDescription()etdouble getPrice(). Ensuite, créez une classePlainPizzaqui implémentePizza- elle retournePlain Pizzacomme description et coûte8.0. Maintenant, construisez trois classes de décorateurs qui enveloppent chacune unePizzaet l'enrichissent :CheeseDecorator- ajoute, Cheeseà la description et1.5au prixPepperoniDecorator- ajoute, Pepperonià la description et2.0au prixMushroomDecorator- ajoute, Mushroomsà la description et1.25au prix
Pizza, accepter unePizzadans son constructeur, et déléguer à la pizza enveloppée avant d'ajouter sa propre contribution.main.dart: Importez votre fichier pizza et démontrez comment les décorateurs s'empilent pour construire une commande personnalisée. Commencez par unePlainPizza, puis enveloppez-la avecCheeseDecorator, puis enveloppez cela avecPepperoniDecorator, et enfin enveloppez le tout avecMushroomDecorator. Affichez la description finale de la pizza, puis affichezTotal: $suivi du prix.
Remarquez comment chaque décorateur s'appuie sur le précédent - vous créez une pizza toute garnie en empilant des décorateurs simples à responsabilité unique !
Sortie attendue :
Plain Pizza, Cheese, Pepperoni, Mushrooms
Total: $12.75Aide-mémoire
Le patron Décorateur (Decorator pattern) ajoute dynamiquement de nouveaux comportements aux objets en les enveloppant dans des objets décorateurs. L'objet original et les décorateurs implémentent tous deux la même interface.
Composants clés :
- Une classe abstraite ou une interface qui définit les méthodes communes
- Une classe concrète qui implémente la fonctionnalité de base
- Des classes décoratrices qui enveloppent l'objet original et l'améliorent
Chaque décorateur :
- Implémente la même interface que l'objet qu'il décore
- Détient une référence à l'objet enveloppé
- Délègue les appels à l'objet enveloppé
- Ajoute son propre comportement avant ou après la délégation
abstract class Coffee {
String getDescription();
double getCost();
}
class SimpleCoffee implements Coffee {
@override
String getDescription() => 'Simple Coffee';
@override
double getCost() => 2.0;
}
class MilkDecorator implements Coffee {
final Coffee _coffee;
MilkDecorator(this._coffee);
@override
String getDescription() => '${_coffee.getDescription()}, Milk';
@override
double getCost() => _coffee.getCost() + 0.5;
}
// Stack multiple decorators
Coffee coffee = SimpleCoffee();
coffee = MilkDecorator(coffee);
coffee = SugarDecorator(coffee);Les décorateurs peuvent être empilés dans n'importe quelle combinaison, créant un comportement flexible sans avoir à créer des sous-classes pour chaque combinaison possible.
Essayez vous-même
import 'pizza.dart';
void main() {
// TODO: Créer une PlainPizza
// TODO: L'envelopper avec CheeseDecorator
// TODO: Envelopper cela avec PepperoniDecorator
// TODO: Tout envelopper avec MushroomDecorator
// TODO: Afficher la description finale de la pizza
// TODO: Afficher "Total: $" suivi du prix
}
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
1Les bases de la POO
Fichiers externesBibliothèques et importsIntroduction à la POOClasses vs ObjetsLe mot-clé thisMéthodesVariables d'instanceBases des constructeursRécapitulatif - Calculatrice simple4Null Safety
Introduction à la Null SafetyNullable vs Non-NullableLes opérateurs ? et !Le mot-clé late et la Null SafetyLes opérateurs Null-AwareLa Null Safety dans les classesRécapitulatif - Système de profil utilisateur7Classes abstraites et Interfaces
Classes abstraitesMéthodes abstraitesInterfaces en DartInterfaces implicitesImplémentation vs ExtensionInterfaces multiplesRécapitulatif - Calculateur de formes10Collections et Génériques
Aperçu des List, Set et MapCollections Type-SafeClasses génériquesMéthodes génériquesContraintes de généricitéIterable & IteratorRécapitulatif - Stockage générique2Les constructeurs en Dart
Constructeur par défautConstructeurs nommésListes d'initialisationConstructeurs constantsConstructeurs factoryConstructeurs de redirectionRécapitulatif - Shape Builder5Encapsulation
Membres publics vs privésLa convention du préfixe _Visibilité au niveau de la bibliothèqueApprofondissement des Getters & SettersMasquage d'informationsRécapitulatif - Dossiers d'étudiants8Mixins
Introduction aux MixinsCréation de MixinsUtilisation de plusieurs MixinsLe mot-clé on dans les MixinsMixin vs HéritageMixin vs InterfaceRécapitulatif - Système Animal11Méthodes spéciales
Redéfinition de toString()Redéfinition de hashCode & ==Interface ComparableMéthode call()Redéfinition de noSuchMethodRécapitulatif - Collection personnalisée14Patrons de conception Partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FabriquePatron ObservateurPatron Stratégie3Propriétés de classe
Membres d'instance vs statiquesChamps Final & ConstVariables LateMéthodes et champs statiquesGetters et SettersRécapitulatif - Gestionnaire de compte bancaire6Héritage
Bases de l'héritageLe mot-clé superRedéfinition de méthodeL'annotation @overrideLe mot-clé final pour les classesConstructeurs et héritageRécapitulatif - Hiérarchie des employés9Polymorphisme
Bases du polymorphismePolymorphisme via les interfacesVérification de type à l'exécutionLes opérateurs is & asMot-clé covariantRécapitulatif - Processeur de paiement12POO Asynchrone
Futures & async/awaitBases des StreamsStream ControllersConstructeurs asynchronesL'asynchrone dans les méthodes de classeRécapitulatif - Data Fetcher15Patrons de conception Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode ModèlePatron ÉtatPatron CompositePatron Repository