Le patron
"Decorator"
61
Riadh BEN HALIMA [Design patterns]
StarCoffee :
Spécification (1/10)
Objectif: Mettre en œuvre un système de gestion des offres
de boisson pour la clientèle de StarCoffee
Besoin: Décrire les ajouts en extra, et calculer le prix total
Thé, Thé-à-la-menthe, Thé-à-la-mente-aux-pignons, etc..
Café, Café-au-Lait, Café-au-Lait-à-la-mousse, etc..
Conception: OO
Concevoir une supère classe Boisson que toutes les autres
classes héritent.
Définir autant de classes qu’il y en a de types de boissons :
[Link], [Link], [Link],
[Link], [Link], [Link],
[Link], etc.
62
Riadh BEN HALIMA [Design patterns]
StarCoffee :
Conception (2/10)
Beverage Retourne la description
Méthode abstraite à définir description
dans les classes dérivées getDescription()
cost()
Tea
Coffee
cost()
cost() CoffeeWithMilk
TeaWithMint
cost()
CoffeeWithMilk cost()
WithWhip
TeaWithMintWit
cost() hPine
cost()
Chaque sous classe implémente la méthode
63
cost() qui représente le prix du boisson
Riadh BEN HALIMA [Design patterns]
StarCoffee :
Problème (3/10)
Problème : En plus des deux produits présentés précédemment,
StarCoffee offre une variété d’autres boissons et condiments: (Si
on offre 2 types de Thé, on doit ajouter plusieurs autres classes
(selon les condiments possibles), etc.
Constat: éclatement du diagramme de classes par un nombre ingérable
de classes Beverage
Description
Solution : Utiliser des variables Milk
Des booléans
d’instance dans la supère classe qui Mint
Pine
représenteront les condiments Whip
getDescription()
(pignon, mousse, lait, menthe). cost()
hasMilk()
setMilk()
hasMint()
setMint()
hasPine()
Des get et set pour les setPine()
booleans des condiments hasWhip()
64
setWhip
Riadh BEN HALIMA [Design patterns]
StarCoffee :
Nouvelle conception (4/10)
Beverage
Description
Milk
Mint
Pine
Whip
getDescription()
cost()
hasMilk()
setMilk()
hasMint()
setMint()
hasPine() Dans chaque classe dérivée,
setPine() on ajoute les condiments
hasWhip() (set), puis c’est la méthode
setWhip() cost() qui calcule le prix
total (en vérifiant avec has)
Coffee Tea
cost() cost()
65
Riadh BEN HALIMA [Design patterns]
StarCoffee :
Un boisson décoré (5/10)
1. On commence par l’objet Tea
La classe Tea hérite de Beverage et possède une
cost() méthode cost() qui calcule le prix du boisson
2. Le client choisit la menthe, alors on crée un objet Mint qui
enveloppe le Tea
L’objet Mint possède une méthode cost() et à travers
le polymorphisme, on peut traiter chaque Beverage
enveloppé dans le Mint comme un Beverage aussi.
cost() cost()
L’objet Mint est un décorateur. Son type est un
miroir de l’objet qu’il décore (Beverage).
66
Riadh BEN HALIMA [Design patterns]
StarCoffee :
Un boisson décoré (6/10)
3. Le client veut aussi des pignons, alors on crée un objet Pine qui
emballe le Mint L’objet Pine est un décorateur, donc il est un
miroir du type Tea et inclut une méthode cost()
cost() cost() cost()
L’objet Tea, enveloppé dans un Mint et un Pine,
reste toujours un Beverage. Alors on peut faire
avec lui ce qu’on peut faire avec les Beverage, y
compris l’invocation de sa méthode cost()
67
Riadh BEN HALIMA [Design patterns]
StarCoffee :
Le coût du boisson décoré (7/10)
L’idée est de calculer le coût en partant du décorateur le plus extérieur
(Pine) et puis, ce dernier délègue le calcul à l’objet décoré, etc.
Pine appelle cost() de Mint
Invocation de la méthode Mint appelle cost() de Tea
cost() du décorateur extérieur
cost() cost() cost()
3000 0.300 1.000
4.300
Pine ajoute son prix au résultat de Tea retourne son prix: 1.000
Mint, et retourne le résultat total: 4.300
Mint ajoute son prix au résultat de cost()
de Tea, et retourne le nouveau total: 1.300
68
Riadh BEN HALIMA [Design patterns]
StarCoffee :
Le patron Decorator (8/10)
Définition: Decorator
Le patron decorator attache des responsabilités
additionnelles à un objet dynamiquement. Les décorateurs
offrent une alternative flexible de sous-classement afin
d’étendre les fonctionnalités.
69
Riadh BEN HALIMA [Design patterns]
StarCoffee :
Le diagramme de classes du patron(9/10)
Chaque composant peut être utilisé à lui
Component seul, ou enveloppé dans des décorateurs
methodA()
methodB() Chaque décorateur possède un composant, qui veut
Le ConcreteComponent est dire que le décorateur possède un attribut qui contient
l’objet qu’on va lui ajouter //autres méthodes une référence d’un composant
dynamiquement des nouveaux
comportements. Il étend
l’objet Component.
ConcreteComponent Decorator
methodA() Component WrappedObj
Les décorateurs implémentent
methodB() methodA() la même interface (ou classe
//autres méthodes methodB() abstraite) que l’objet qu’ils
décoreront
//autres méthodes
ConcreteDecoratorA ConcreteDecoratorB
Le ConcreteDecorator possède
un attribut qui représente l’objet
methodA() Object newState
qu’il décore. Il peut ajouter ses
propres méthodes et attributs. methodB() methodA()
newbehavior() methodB()
//autres méthodes //autres méthodes
70
Riadh BEN HALIMA [Design patterns]
StarCoffee :
La conception finale (10/10)
Beverage
Beverage agit comme notre
classe abstraite component description
getDescription()
cost();
//autres méthodes
Coffee Tea CondimentDecorator Le constructeur du
cost() cost() Beverage beverage décorateur prend le
boisson à décorer en
getDescription() entrée et retourne le
boisson décoré
Les deux composants
concrets, 1 par type de boisson
Whip Milk Mint Pine
cost() cost() cost() cost()
71
Riadh BEN HALIMA [Design patterns]
Récapitulatif (1/2)
Bases de l’OO: Abstraction, Encapsulation, Polymorphisme & Héritage
Principes de l’OO
Encapsuler ce qui varie
Favoriser la composition sur l’héritage
Programmer avec des interfaces et non des implémentations
Opter pour une conception faiblement couplée
Les classes doivent être ouvertes pour les extensions et fermées pour
les modifications
Patron de l’OO
Strategy: définit une famille d’algorithmes interchangeables
Observer: définit une dépendance1-à-plusieurs entre objets.
decorator: attache des responsabilités additionnelles à un objet
dynamiquement. Les décorateurs offrent une alternative flexible de sous-
classement afin d’étendre les fonctionnalités.
72
Riadh BEN HALIMA [Design patterns]
Récapitulatif (2/2)
L’héritage est une forme d’extension, mais il n’est pas nécessairement
la meilleure manière pour obtenir la flexibilité dans notre conception
Le patron decorator implique un ensemble de classes de décorations
qui sont utilisées pour envelopper les composants concrets.
Les classes décorateurs reflètent le type de composant qu’ils
décorent.
Les décorateurs changent le comportement de leurs composants tout
en ajoutant des nouvelles fonctionnalités après/avant (ou à la place de)
l’appel des méthodes des composants
On peut envelopper un composant dans n’importe quel nombre de
décorateurs
Les décorateurs sont transparents par rapport au client du
composant
73
Riadh BEN HALIMA [Design patterns]
Exercice (1/5)
1. Comment faire pour obtenir un café avec "double mousse"?
2. StarCoffee a ajouté un nouveau boisson (Citronnade) au système,
comment procéder pour l’inclure dans la conception actuelle?
3. StarCoffee veut introduire des tailles pour ses menus: SMALL,
MEDIUM et LARGE. Comment prendre en charge cette nouvelle
spécification, si la taille modifie seulement les prix des composants
concrets?
74
Riadh BEN HALIMA [Design patterns]
Exercice (2/5)
Avec le patron decorator, le package [Link] doit donner plus de
sens, puisqu’il se base largement sur ce patron.
FileInputStream est un composant
décoré. Le package [Link] offre plusieurs
objets pour la lecture des octets:
FileInputStream,
StringBufferInputStream,
ByteArrayInputStream, etc.
BufferedInputStream est un décorateur concret. Il
LineNumberInputStream est aussi ajoute deux comportements: (i) il tamponne les
un décorateur concret. Il ajoute la entrées afin d’améliorer la performance, et (ii)
capacité de compter les nombres ajoute la méthode readLine() pour lire une ligne de
de lignes en lisant les données. caractères à la fois.
75
Riadh BEN HALIMA [Design patterns]
Exercice (3/5): Décoration de [Link]
1. Ecrire un décorateur qui convertit tous les caractères majuscules
en minuscules dans le flux d’entrée (InputStream).
Abstract Component
Conctrete
Component InputStream
Abstract Decorator
FileInputStream StringBufferInputStream FilterInputStream
ByteArrayInputStream
PushBackInputStream BufferedInputStream
Decorator
DataInputStream LineNumberInputStream
76
Riadh BEN HALIMA [Design patterns]
Exercice (4/5)
L’objectif de cet exercice est de mettre en œuvre un système
flexible de gestion des offres de voiture pour la clientèle de
StarCar. Le besoin de cette société se résume à décrire les
options demandées par le client (VitreElectrique, AirBag et ABS)
et inclure son cout au prix total de la voiture choisie. Deux types
de voiture sont gérés par la société, à savoir, camionnette et
berline. Chaque voiture est caractérisée par un cout et une
description textuelle. Le prix de chaque type de voiture ainsi que
celui de chaque option est à fixer au moment de la création.
En utilisant le patron Decorator, donnez le diagramme de classes
de l’application CarStar. (Précisez les méthodes et les attributs,
correspondant au bout de code présenté dans le slide suivant)
77
Riadh BEN HALIMA [Design patterns]
Exercice (5/5)
public static void main(String[] args) {
Voiture v1=new Camionnette ("P404",10000);
Voiture v2=new Berline ("P407",20000);
v1=new ABS(v1, 800);//800 représente le prix de l’option ABS
v2=new VitreElectrique(v2, 1000); // 1000 représente le prix de l’option
v2=new AirBag(v2, 1200); // 1200 représente le prix de l’option
[Link]("La voiture est une "+[Link]());
//affiche: La voiture est une P404 avec ABS
[Link]("Son prix est:"+ [Link]());
//affiche: Son prix est 10800
[Link]("La voiture est une "+[Link]());
//affiche: La voiture est une P407 avec VitreElectrique avec AirBag
[Link]("Son prix est:"+ [Link]());
//affiche: Son prix est 22200
}
78
Riadh BEN HALIMA [Design patterns]