1 2
Historique Motivation
Notion de « patron » d’abord apparue en architecture : Pourquoi définir des patrons de conception
l’architecture des bâtiments Construire des systèmes plus extensibles, plus robustes au changement
la conception des villes et de leur environnement Capitaliser l’expérience collective des informaticiens
L’architecte Christopher Alexander le définit comme suit: Réutiliser les solutions qui ont fait leur preuve
«Chaque modèle [patron] décrit un problème qui se manifeste constamment Identifier les avantages/inconvénients/limites de ces solutions
dans notre environnement, et donc décrit le cœur de la solution de ce problème,
d’une façon telle que l’on peut réutiliser cette solution des millions de fois.» [Livre:
Savoir quand les appliquer
The Timeless Way of Building, Oxford University Press 1979]
Complémentaire avec les API
Projeter la notion de patron à du logiciel : "design pattern" Une API propose des solutions directement utilisables
premiers patrons à partir de 1987 (partie de la thèse de Erich Gamma)
Un patron explique comment structurer son application avec une API
puis Richard Helm, John Vlissides et Ralph Johnson («Gang of Four, GoF»)
premier catalogue en 1993 : Elements of Reusable Object-Oriented Software
Patron de conception dans le cycle de développement
Intervient en conception détaillée
Vocabulaire:
modèles de conception= patrons de conception= motifs de conception= design Reste indépendant du langage d’implantation
patterns
3 4
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Intérêt et utilisation des patrons de
Définition conception
Définition : "Un patron de conception (design pattern) La meilleure manière d’utilisation des patrons de conception est de les mémoriser
en tête, puis reconnaitre leurs emplacements et les appliquer dans la conception
décrit une structure commune et répétitive de des applications
composants en interaction (la solution) qui résout un
problème de conception dans un contexte particulier
Les concepts de l’orienté objet tels que
Au lieu de la réutilisation de code, on parle de la l’abstraction, l’héritage, et le polymorphisme ne
réutilisation de l’expérience avec les patrons te rendent pas un bon concepteur!
Un concepteur pense à créer une conception
Un bon patron de conception : flexible qui est maintenable et fait face aux
résout un problème changements
5 6
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
7 8
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
SimUDuck:
Conception (1/22)
Objectif: développement d’un jeu de simulation d’un bassin
pour les canards
Besoin: nager, cancaner, afficher, etc..
Le patron
Supporter une large variété de canards
Conception: OO
"Strategy" Une supère classe Canard (Duck) dont tous les canards héritent
Duck
quack()
Abstract swim()
display()
display() display()
9 10
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
SimUDuck :
SimUDuck : Innovation (2/22) Problèmes (3/22)
Objectif: Innovation (pour impressionner et vendre +) Besoin: Au moment de la démonstration du simulateur, on nous
demande de simuler des canards en caoutchouc
Besoin: simuler le vol des canards!
Conception: OO
Conception: OO Ajouter la classe RubberDuck qui hérite de la supère classe Duck
Ajouter la méthode fly() à la supère classe
Duck Duck
! Le caoutchouc ne vole pas
quack() quack()
swim() swim()
Modification apportée fly() Hériter par tous les canards fly()
display() display()
display()
display() display() display() display()
quack(){
Redéfinir à squeak
}
11 12
? Le caoutchouc ne cancane pas
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
SimUDuck : SimUDuck :
Constat (4/22) Solution?? (5/22)
Problème 1: Le canard en caoutchouc ne cancane pas! Problème 2: Le canard en caoutchouc ne vole pas! Toutefois, il
hérite la méthode fly() de la supère classe Duck!
Solution : Redéfinir la méthode quack() à squeak()
(résolu) Solution: Redéfinir la méthode fly() de RubberDuck
RubberDuck
Problème 2: Le canard en caoutchouc ne vole pas! display()
Toutefois, il hérite la méthode fly() de la supère classe quack(){ squeak }
fly(){
Duck! Redéfinir à rien
}
Constat:
Ce que nous avons cru une utilisation formidable de l’héritage Question: est ce que c’est résolu pour tous types de canards?
dans le but de la réutilisation, s’est terminé mal au moment de
la mise à jour!
Une mise à jour du code a causé un effet global sur
l’application!
13 14
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
SimUDuck : SimUDuck :
Un autre canard (6/22) Des interfaces? (7/X)
Nouveau type de canard: Canard en bois Hypothèse: On nous demande de mettre à jour SimUDuck tous les 6 mois: La
spécification demeure changeante
Problèmes levés:
Vérifier fly() et quack() pour chaque nouveau canard
Ce canard ne cancane pas Ré-écrire (si besoin) fly() et quack()
Ce canard ne vole pas Solution possible pour contourner le problème: les interfaces
Solution: redéfinir (une autre fois) les méthodes quack() et fly() Interface
Duck
WoodenDuck
Flyable Quackable swim()
display()
quack(){ fly() quack() display()
Redéfinir à rien
}
fly(){
Redéfinir à rien
} MallardDuck RedHeadDuck RubberDuck WoodenDuck
Inconvénients de l’utilisation de l’héritage display() display() display() display()
fly() fly() quack()
Il est difficile de connaitre le comportement de tous les canards quack() quack()
Un changement non-intentionnelle, affecte les autres canards
15
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
16 Que dites vous de cette conception ?
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
SimUDuck : SimUDuck :
Inconvénients (8/22) Moment de réflexion (9/22)
Duck
Flyable Quackable swim()
Pas toutes les sous-classes qui ont besoin de voler (fly)
fly() quack() display() ou de cancaner (quack)
L’héritage n’est pas la bonne solution
Les interfaces Flyable et Quackable résolvent une
MallardDuck RedHeadDuck RubberDuck WoodenDuck partie du problème
display() display() display() display()
fly() fly() quack() Détruit complètement la réutilisation du code pour ces
quack() quack() comportements
Constat: La maintenance et la mise à jour représentent un vrai calvaire
Duplication de code: méthodes fly() et quack() dans les sous-classes Supposant qu’il existe plus qu’une façon de voler
Autant d’interfaces tant qu’il y a un ensemble de canards ayant exclusivement un
comportement commun (pondre: lay() pour les canards qui peuvent déposer un œuf) Maintenance plus difficile…
Problème: si on veut modifier/adapter légèrement la méthode fly(), il faut le faire
pour toutes les classes des canards (10 classes, 100, ou +)
17 18
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
SimUDuck : SimUDuck :
Solution (10/22) Principe de conception (11/22)
Solution:
Design pattern : solution ultime, cheval blanc, sauveur… Règle 1: Identifier les aspects variables de mon
application et les séparer de ce qui reste invariant
Trouvons une solution avec l’"ancienne-mode" et ce en C’estla base de tous les patrons de conception
applicant les bonnes principes de la conception OO Système plus flexible+peu de conséquences inattendues
Concevoir une application, ou un besoin de Mise en œuvre
modification/changement peut être appliqué avec le
moindre possible d’impact sur le code existant Prendre la partie qui varie et l’encapsuler. De cette
façon, un changement ultérieur affecte la partie
Donner des raisons de changement de code
variable, sans toucher à celle invariable
dans votre application
19 20
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
SimUDuck : SimUDuck :
Séparation (12/22) Conception des comportements (13/22)
La classe Duck est toujours la supère classe Conception initiale: l’inflexibilité des comportements a engendré
des troubles
Les comportements fly() et quack() sont retirés, et mis dans une
autre structure On veut affecter les comportements aux instances des Ducks
tout en permettant:
Comportements La création d’une instance (MallardDuck),
Mettre dehors ce qui varie L’initialisation avec un type de comportement (type de vol)
La possibilité de changer le type de vol dynamiquement (?)
SimUDuck : SimUDuck :
Conception des comportements (14/22) Intégration des comportements(15/22)
FlyBehavior QuackBehavior
fly() quack()
La supère classe Duck, dont hérite tous les canards
Duck Variable d’instance
du type INTERFACE
FlyBehavior fbehavior;
FlyWithWings FlyNoWay Quack Squeak MuteQuack QuackBehavior qbehavior;
fly() { fly() { quack(){ quack(){ quack(){
//squeak //rien-ne performQuack()
//vol //rien-ne vol pas //quack
//rubber //cancane pas swim()
} } }
} } display()
performFly()
Conséquences: //….
On peut ajouter un nouveau comportement sans modifier ni le code des Ces méthodes remplacent quack() et fly()
comportements existants, ni le code des classes des canards qui utilisent les
comportements voler/cancaner La clé: le canard délègue les comportements fly et quack,
Avec cette conception, d’autres objets peuvent réutiliser le comportement fly et au lieu d’utiliser les méthodes fly() et quack() définies
quack, parce qu’ils ne sont plus cachés dans les classes canards.
dans la supère classe Duck.
23 24
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
SimUDuck : SimUDuck :
Implémentation de la supère classe(16/22) Implémentation d’un canard (17/22)
La supère classe Duck, dont hérite tous les canards
Cette classe inclut les méthodes réalisant
le comportement fly et quack, par héritage
public class Duck{ (performQuack(), etc..)
QuackBehavior qbehavior; Chaque type de canard initialise ces attributs
FlyBehavior fbehavior; selon ses besoins.
//… (par FlyWithWings pour le MallardDuck )
public class MallardDuck extend Duck{
public void performQuack(){ public MallardDuck (){ Initialisation des attributs déclarés
qbehavior.quack(); fbehavior = new FlyWithWings(); dans la supère classe Duck
} qbehavior = new Quack();
//..
}
} Grace au polymorphisme, la bonne méthode sera public void display(){
invoquée dans la sous-classe du type de canard. System.out.println("Je suis un canard Mallard");
(Déléguée à la classe gérant le comportement) }
}
25 26
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
SimUDuck : SimUDuck :
Tester le code du canard(18/22) Le comportement dynamique (19/22)
Développer et compiler [Utiliser NetBeans/Eclipse]: Changement dynamique de comportement
La classe abstraite Duck (Duck.java) Ajouter les méthodes: setFlyBehavior() et setQuackBehavior()
Le comportements: FlyBehavior.java, FlyWithWings.java et Développer le canard RedHeadDuck (RedHeadDuck.java)
FlyNoWay.java, Implanter le nouveau comportement "vole-force-fusée"
Le comportement : QuackBehavior.java, Quack.java, Squeak.java et FlyRocketPowered (FlyRocketPowered.java)
MuteQuack.java
Tester le nouveau canard dans un main RedHeadSim.java
Les classes MallardDuck.java et WoodenDuck.java
Changer le comportement "voler" de FlyWithWings à
Tester toutes les méthodes des canards créés dans un main: FlyRocketPowered. Penser à utiliser le setter afin d’obtenir ces
MallardDuckSim.java deux affichages: "Je peux voler" & "Je vole comme une fusée"
SimUDuck :
Notre premier patron (22/22) Les avantages du patron
31 32
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
L’implémentation du patron Représentation du patron
33 34
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
35 36
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Exercice (1/2) Exercice (2/2)
Ci-dessous, on donne l’ensemble de classes et interfaces d’un jeu 1. Arranger les classes
d’action et d’aventure. Il y a des classes d’individus avec des
classes pour les comportements d’armes que les individus
2. Identifier les classes, les classes abstraites des interfaces
peuvent utiliser. Chaque individu peut utiliser une seule arme à la 3. Relier les entités pas des flèches ou:
fois, mais peut la changer à tout moment durant le jeu. La tâche 1. représente extends
demandée est d’ordonner le tout. 2. représente implements
Individu ComportementCouteau Chevalier 3. représente has-a
ComportementArme arme; UtiliseArme(){\\abattre avec Display(){….} 4. Mettre la méthode setArme() dans la classe
combattre() couteau }
Display () correspondante
Archer ComportementArc&Fleche
Reine
Display(){….} UtiliseArme(){\\abattre avec
5. Implémenter et tester cette conception dans un main.
Display(){….} arc et flèche} Penser à changer dynamiquement le comportement de
ComportementArme l’archer après avoir finir ces arcs.
setArme(ComportementArme ca) {
UtiliseArme();
this.arme=ca; ComportementEpee
}
UtiliseArme(){\\abattre avec
37 épée} 38
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Station météo:
Spécification (1/14)
Objectif: Construire une nouvelles génération de stations
d’observation météo sur Internet
Besoin: Afficher les conditions courantes, les statistiques
Le patron
météorologique et les prévisions météo.
Poursuivre les conditions météorologiques (Température,
"Observer"
Humidité, Pression, etc.)
On veut mettre en œuvre une API (ENIS-METEO) de façon
que d’autres développeurs peuvent écrire leur propre afficheur
de météo.
Conception: OO
Une classe WeatherData qui récupère les données de la station
météo (ENIS-METEO) et les offre aux afficheurs.
39 40
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Station météo: Station météo:
Analyse (2/14) Conception (3/14)
WeatherData Ces trois méthodes retournent les
mesures les plus récentes
Afficher getTemperature()
Capteur getHumidity()
d’humidité Tirer (pull) Cette méthode sera appelée chaque
getPressure()
fois qu’on met à jour les mesures
measurementChanged()
Capteur de \\autre méthodes
température
Weather Station
WeatherData Nous allons développer trois afficheurs:
Object
Capteur de Conditions conditions courantes (CurrentConditionsDisplay.java)
pression courantes
statistiques météorologique (StatisticsDisplay.java)
Le fournisseur ENIS-METEO A implémenter prévisions météo (ForecastDisplay.java)
Il faut créer une application qui utilise l’objet WeatherData afin de Rq: On ne s’intéresse pas à la manière dont les variables sont fixées.
mettre à jour trois afficheurs: les conditions courantes, les statistiques On suppose que l’objet WeatherData connait comment les mettre à
météorologiques & les prévisions météo. jour à partie de la station ENIS-METEO
41 42
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
45 46
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Nouvel entier
47 48
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Station météo: Station météo:
Patron Observer : Notification (10/14) Le patron Observer (11/14)
Définition: Observer
Maintenant, le Dog n’est plus un
observer, il ne reçoit plus les
Le patron observer définit une dépendance 1-à-plusieurs
notifications du Sujet entre des objets de façon que à chaque changement de
l’état de l’objet, tous ces dépendants sont notifiés et mis à
jour automatiquement.
12
1-à-plusieurs
Nouvel entier
8
CurrentConditionsDisplay
subject
WeathetData update()
display()
registerObserver(){…}
removeObserver() {…}
notifyObservers() {…}
StatisticsDisplay ForecastDisplay
getTemperature()
getHumidity() update() update() Le patron de conception observateur/observable est utilisé pour envoyer
getPressure() display() display() un signal à des modules qui jouent le rôle d'observateur.
measurementChanged() En cas de notification, les observateurs effectuent alors l'action adéquate en
fonction des informations qui parviennent depuis les modules qu'ils
Les trois afficheurs pointent sur le WeatherData afin
de permettre leur inscription et leur désinscription
observent (les "observables").
53 54
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Exercice (2/2)
Utiliser le pattern Observer pour définir les interactions entre Chrono, Display et
Sonnerie
Donnez le diagramme de classes et l’implémentation.
59 60
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
StarCoffee : StarCoffee :
Spécification (1/10) Conception (2/10)
Objectif: Mettre en œuvre un système de gestion des offres Beverage Retourne la description
Description
de boisson pour la clientèle de StarCoffee Méthode abstraite à définir
dans les classes dérivées getDescription()
Besoin: Décrire les ajouts en extra, et calculer le prix total cost()
StarCoffee : StarCoffee :
Problème (3/10) Nouvelle conception (4/10)
Problème : En plus des deux produits présentés précédemment, Beverage
StarCoffee offre une variété d’autres boissons et condiments: (Si Description
Milk
on offre 2 types de Thé, on doit ajouter plusieurs autres classes Mint
(selon les condiments possibles), etc. Pine
Whip
Constat: éclatement du diagramme de classes par un nombre ingérable getDescription()
cost()
de classes Beverage hasMilk()
Description
Solution : Utiliser des variables Milk
setMilk()
Des booléans hasMint()
d’instance dans la supère classe qui Mint
Pine setMint()
Dans chaque classe dérivée,
représenteront les condiments Whip hasPine()
on ajoute les condiments
getDescription() setPine()
(pignon, mousse, lait, menthe). cost() hasWhip() (set), puis c’est la méthode
hasMilk() setWhip() cost() qui calcule le prix
setMilk() total (en vérifiant avec has)
hasMint()
Coffee Tea
setMint()
hasPine() cost() cost()
Des get et set pour les setPine()
booleans des condiments hasWhip()
63 64
setWhip LOUATI [Design patterns]
Riadh BEN HALIMA&Wajdi Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
StarCoffee : StarCoffee :
Un boisson décoré (5/10) Un boisson décoré (6/10)
1. On commence par l’objet Tea 3. Le client veut aussi des pignons, alors on crée un objet Pine qui
La classe Tea hérite de Beverage et possède une emballe le Mint L’objet Pine est un décorateur, donc il est un
cost() méthode cost() qui calcule le prix du boisson miroir du type Tea et inclut une méthode cost()
StarCoffee : StarCoffee :
Le coût du boisson décoré (7/10) Le patron Decorator (8/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. Définition: Decorator
Pine appelle cost() de Mint Le patron decorator attache des responsabilités
Invocation de la méthode Mint appelle cost() de Tea additionnelles à un objet dynamiquement. Les décorateurs
cost() du décorateur extérieur offrent une alternative flexible de sous-classement afin
d’étendre les fonctionnalités.
cost() cost() cost()
1.000
Pine ajoute son prix au résultat de Tea retourne son prix: 0.500
Mint, et retourne le résultat total: 1.000
Mint ajoute son prix au résultat de cost()
de Tea, et retourne le nouveau total: 0.700
67 68
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
StarCoffee : StarCoffee :
Le diagramme de classes du patron(9/10) La conception finale (10/10)
Chaque composant peut être utilisé à lui
seul, ou enveloppé dans des décorateurs Beverage
Component Beverage agit comme notre
classe abstraite component description
Component
methodA() getDescription()
methodB() Chaque décorateur possède un composant, qui veut cost();
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
//autres méthodes
dynamiquement des nouveaux
comportements. Il étend
l’objet Component.
73 74
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Le patron
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
"Adapter"
System.out.println("La voiture est une "+v1.getDescription());
//affiche: La voiture est une P404 avec ABS
System.out.println("Son prix est:"+ v1.cost());
//affiche: Son prix est 10800
System.out.println("La voiture est une "+v2.getDescription());
//affiche: La voiture est une P407 avec VitreElectrique, AirBag
System.out.println("Son prix est:"+ v2.cost());
//affiche: Son prix est 22200
77 78
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
79 80
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Le patron adapter : Le patron adapter :
Dinde et Canard (3/7) L’adaptateur du dinde(4/7)
Supposant que le dinde marche et cancane comme le canard Supposons qu’on a un manque de canards et on va utiliser des
Un canard peut cancaner et voler Une simple implémentation du comportement du canard
dindes à leur pace Il faut écrire un "adapter"
Le patron adapter :
Testons l’adaptateur(5/7) Le patron adapter (6/7)
Adapter
public class TestAdapter{
public static voir main (String arg[]) Définition:
{
MallardDuck mallard= new MallardDuck(); Le patron Adapter convertit l’interface d’une classe à
WildTurkey wild = new WildTurkey();
Duck turkeyAdapter = new TurkeyAdapter(wild);
une autre interface que le client attend. Les adaptateurs
test(mallard); permettent aux classes, aillant des interfaces incompatibles,
de travailler ensemble.
test(turkeyAdapter);
}
static void test(Duck duck)
{
duck.quack();
duck.fly();
}
}
83 84
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Adapter :
Le patron adapter (6/7)
Le diagramme de classes du patron (7/7)
<<interface>>
Client Target
Request() L’Adapter implémente
l’interface Target
85 86
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Le patron Facade:
Démarrer un home-cinéma (1/4)
Tuner Amplifier
tuner DVDPlayer
Démarrer le HC: amplifier dvdPlayer
on() amplifier
baisser la lumière off() ….
Le patron
on()
on()
allumer l’écran setAm() off()
off()
setFM() play()
setCD()
démarrer l’ampli … stop()
"Façade"
setDVD()
pause()
démarrer le DvDplayer …
…
le brancher avec l’mpli
Screen
jouer le DvD
up()
etc.. down()
Façade:
Le diagramme de classes du patron (4/4) Testons nos connaissances!
Une interface unifiée et
simple à utiliser
Client Façade
Patron Rôle
Un client heureux car
son travail devient Decorator Convertit une interface en une
facile grâce à la façade Les classes des sous-systèmes
autre
Systèmes complexes
Facade Rend les interfaces simples
91 92
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Singleton :
Spécification (1/12)
Objectif: Créer un type d’objet pour lequel on crée
seulement une seule instance
C’est le patron ayant le diagramme de classes le plus simple
Le patron
Il y a plusieurs objets dont on a besoin d’une seule instance:
pool d’impression, boite de dialogue, objet qui manipule les
"Singleton" préférences, objet de logging, objet agissant comme pilote de
carte graphique/imprimante…
La création de plus d’une instance de ces objets est une
source de problème, telle que la sur-utilisation des
ressources, des comportements incorrectes de programme,
des résultats inconsistants, etc.
93 94
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Singleton : Singleton :
Créer un singleton (2/12) Créer un singleton (3/12)
Comment créer un seul objet? Comment je peux appeler cette méthode (pour créer une
New MonObjet()
instance) si je n’ai pas d’instance?
Et si un autre objet veut créer un MonObjet? Est-ce qu’il peut static
public class MonObjet{
appeler new sur MonObjet une autre fois? Que signifie ce code. public static MonObjet getInstance() {
}
Oui }
Pour toute classe, est ce qu’on peut l’instancier plus qu’une fois? C’est une méthode statique qui peut être appelée à partir du nom de la
Oui (il faut que la classe soit publique) classe : MonObjet.getInstance()
Que signifie ce code ? Si on met les choses ensemble, est ce qu’on peut instancier
public class MonObjet{
C’est une classe qui ne peut pas être instanciée, private MonObjet() {}
MonObjet? public class MonObjet{
car elle possède un constructeur privé } private MonObjet(){ }
public static MonObjet getInstance() {
return new MonObjet();
Qui peut utiliser ce constructeur? }
Le code de MonObjet est le seul code qui peut l’appeler (dans une méthode) }
Comment faire pour créer une seule instance?
95 96
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Singleton : Singleton :
Implémentation du patron (4/12) L’usine de chocolat (5/12)
Nous avons une variable statique public class ChocolateBoiler{
pour stocker notre instance private boolean empty; Le code démarre lorsque
private boolean boiled; la casserole est vide
public class Singleton { public ChocolateBoiler() {
empty=true; boiled=false;
private static Singleton uniqueInstance; Pour remplir la casserole, elle
}
private Singleton() {} Le constructeur est déclaré privé. public void fill(){ doit être vide. Lorsqu’elle est
public static Singleton getInstance(){ Seulement la classe Singleton qui if (empty){ pleine, on met empty à false.
if (uniqueInstance == null) peut instancier cette classe //remplir la casserole avec du lait/chocolat
uniqueInstance = new Singleton(); empty=false; boiled=false;
}
} Pour vide la casserole, elle doit
return uniqueInstance; Cette méthode nous offre une manière public void drain(){ être pleine et déjà mixée. une
} pour instancier la classe Singleton if (!empty && boiled){ fois vidée, on met empty à true.
//vider la casserole
empty=true;
Si uniqueInstance n’est pas à nul, ça veut dire }
qu’elle a été créée précédemment }
public static void main(String args[]) { public void boil(){ Pour mixer le contenu de la
Singleton s= Singleton.getInstance(); if (!empty && !boiled){ casserole, elle doit être pleine et
} //faire bouillir non déjà mixée. Lorsqu’elle est
} boiled=true; pleine, on met boiled à true.
}
}
}
97 98
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Singleton : Singleton :
L’usine de chocolat (6/12) Le patron Singleton(7/12)
Améliorer le code de l’usine de chocolat en le transformant en Singleton
public class ChocolateBoiler { Définition: Singleton
private boolean empty; Le patron Singleton assure une seule instance pour une
private boolean boiled;
private static ChocolateBoiler uniqueInstance; classe, et offre un point d’accès global à cette classe.
private ChocolateBoiler() {
empty=true; boiled=false;
}
99 100
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Singleton: Singleton :
Le diagramme de classes du patron (8/12) Problème des threads (9/12)
Supposant que nous avons deux threads qui vont exécuter la méthode
getInstance(). Est-ce qu’il y a un cas où on crée 2 instances?
Thread-1 Thread-2 Valeur de
La variable de classe uniqueInstance UniqueInstance
tient la seule instance du Singleton public static ChocolateBoiler
getInstance() null
Singleton public static ChocolateBoiler null
getInstance()
- static uniqueInstance
if (uniqueInstance == null) null
//autre données utiles
if (uniqueInstance == null) null
+ static getInstance()
uniqueInstance = new Object1
//autre méthodes utiles ChocolateBoiler();
La méthode getInstance() est statique. Object1
return uniqueInstance;
C’est une méthode de classe qu’on peut
y accéder partout dans le code avec uniqueInstance = new Object2
Singleton.getInstance(). Il s’agit d’une ChocolateBoiler();
instanciation facile de cette classe return uniqueInstance; Object2
101 102
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Singleton : Singleton :
Gestion du multi-threading (10/12) Gestion du multi-threading (11/12)
Solution 1: synchroniser l’accès à la méthode getInstance() Solution 2: création au moment de la définition de la variable de classe
Initialisation par le JVM avant accès des threads
public class Singleton { public class Singleton {
private static Singleton uniqueInstance; private static Singleton uniqueInstance = new Singleton();
Un seule thread peut accéder,
private Singleton() {} à la fois, à cette méthode private Singleton() {}
Inconvénient: synchronized réduit la performance d’un facteur de 100 La JVM crée une instance de Singleton lors du chargement de la classe. La JVM
Si la méthode getInstance() n’est pas critique pour notre application, on peut garantit que l’instance va être créée avant que les threads accèdent la variable
se contenter de cette solution statique uniqueInstance.
103 104
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Singleton :
Gestion du multi-threading (12/12) Récapitulatif (1/2)
Solution 3: réduire l’utilisation de la synchronisation dans getInstance() Bases de l’OO: Abstraction, Encapsulation, Polymorphisme & Héritage
Le mot clé volatile assure que les threads gèrent la variable Principes de l’OO
uniqueInstance correctement au moment de son initialisation Encapsuler ce qui varie
public class Singleton {
Favoriser la composition sur l’héritage
private volatile static Singleton uniqueInstance;
Programmer avec des interfaces et non des implémentations
private Singleton() {} Opter pour une conception faiblement couplée
Les classes doivent être ouvertes pour les extensions et fermées pour les
public static Singleton getInstance(){ On synchronise seulement la
if (uniqueInstance == null) { première fois modifications
synchronized(Singleton.class) { Dépendre des abstractions. Ne jamais dépendre de classes concrètes
if (uniqueInstance == null) Patron de l’OO
uniqueInstance = new Singleton(); Strategy: définit une famille d’algorithmes interchangeables
}
Observer: définit une dépendance1-à-plusieurs entre objets.
}
return uniqueInstance; decorator: attache des responsabilités additionnelles à un objet dynamiquement.
} Abstract Factory: offre une interface de création de familles d’objets
//autre méthodes utiles Factory Method: définit une interface de création des objets
}
Singleton: assure à une classe une seule instance et lui offre un point d’accès global
*volatile: inclus à java depuis jdk5
105 106
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Récapitulatif (2/2)
Le patron singleton assure la création d’au plus une instance d’une
classe de notre application
Le patron offre aussi un seul point d’accès global à cette instance
L’implémentation Java du patron utilise un constructeur privé une Le patron
méthode statique combinée avec une variable statique
Le développeur examine la performance et les contraintes des
ressources et choisit soigneusement une implémentation pour une
"Command"
application multi-thread
107 108
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Home-Automation : Home-Automation :
Spécification (1/28) Analyse du contrôleur distant (2/28)
Objectif: Mettre en œuvre un système de contrôle distant
d’un ensemble d’appareils dans une maison Il y a des boutons on et off
pour chaque slot
Besoin: programmer les fonctionnalités d’un contrôleur
distant (avec 7 slots) selon des classes (prédéfinies par le Ces deux boutons ont utilisés
pour contrôler la télévision
vendeur) de gestion des appareils installés dans la maison. Il y a 7 slots à programmer.
On met un appareil différent Ces deux boutons ont utilisés
Conception: OO dans chaque slot, et on le pour contrôler la chaine Stéreo,
contrôle à travers les boutons etc…
Prévoir les relations entre les boutons du contrôleur distant
(ON-OFF) avec les fonctionnalités des appareils installés:
setTemperature(), setVolume(), setDirection(), etc.. Un bouton global UNDO
pour annuler l’action du
dernier bouton activé
Contrôleur distant
109 110
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Home-Automation : Home-Automation :
Les classes du vendeur (3/28) Discussion de la conception (4/28)
Les classes du vendeur nous donnent une idée sur les fonctionnalités
On s’attendait à des classes avec des méthodes on()-off()
des appareils installés dans la maison :
pour bien correspondre avec le contrôleur distant
Faucet AirCondition
Stereo Appliance
setTemperature()
C’est important de voir ça comme séparation des
on() openValue()
on()
off() closeValue() préoccupation : le contrôleur doit savoir comment
off()
setCD()
Security
interpréter l’appui sur le bouton et créer des requêtes, mais
setRadio()
setVolume() Light GarageDoor arm()
il ne doit pas connaitre beaucoup sur les appareils et leurs
on() up() desarm() manières de fonctionnement (comment allumer une lampe)
off() down()
stop() En d’autre terme, le contrôleur émet des requêtes génériques
GardenLight lightOn() OutDoorLight
CeilingLight
Une entité prendra en charge la transformation de cette requête
lightOff() on()
setDuskTime() en action
setDawnTime() on() off()
manualOn() off()
dim() Sprinkler
manualOff()
waterOn()
waterOff()
111 112
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Home-Automation : Command :
Discussion de la conception (4/28) Commander un dîner (5/28)
Comment émettre des requêtes à des objets Order
sans rien connaître des opérations demandées ?
ou sans rien connaître de celui à qui la requête est destinée ?
Command : Command :
Etudes des interactions (6/28) Je veux
Les rôles et les responsabilités (7/28)
un burger, La commande (papier) est une requête pour préparer le repas
un shake et S’il s’agit d’un objet, il peut être passé de la serveuse au comptoir
createOrder() des frites
Son interface consiste à une seule méthode orderUp(), qui encapsule les actions
nécessaires pour préparer le repas
Il y a toutes les
instructions La serveuse n’a besoin de savoir comment préparer le repas!
nécessaires pour takeOrder() La tâche de la serveuse est de prendre la commande et d’invoquer
préparer le repas la méthode orderUp() dessus
La méthode takeOrder() de la serveuse peut être paramétrée avec différentes
La serveuse récupère la
orderUp() commandes de plusieurs clients. Ceci ne la dérange pas car elle sait que orderUp()
commande et la met sur le
supporte sa commande
comptoir, et la lance
Le cuisinier possède les connaissances nécessaires pour préparer le
repas
Suite à l’invocation de orderUp(), le cuisinier implémente toutes les méthodes
Le cuisinier suit nécessaires pour créer le repas
makeBurger(), makeShake(), makeShips()
les instructions
de la commande
Noter qu’il est complètement découplé de la serveuse
La serveuse encapsule les détails du repas dans la commande
Le cuisinier prend ses instructions de la commande, et il n’a pas besoin de la contacter
output()
115 116
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Command : Command :
Du dîner vers le patron Commande (8/28) Correspondance (9/28)
Le client crée la commande.
L’objet Command offre une
public void execute(){
receiver.action1();
action1()
action2()
La commande consiste à un Dîner Command Pattern
….. ensemble d’actions et un receveur
seule méthode execute() receiver.action2();
}
qui encapsule les actions create Serveuse Command
createCommandObject( ) Command
Object (Waitress)
execute()
Cuisinier execute()
setCommand() (Order-Cook)
Plus tard, le client
demande à l’invoker orderUp() Client
setCommand()
d’exécuter sa commande
execute() Commande invoke
execute()
(Order)
de ceci résulte Client Receiver
action1(), action2() action1() l’invocation des (Customer)
L’invoker appelle la méthode action2() actions sur le
execute() de l’objet Command ….. Receiver takeOrder() setCommand()
117 118
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Command : Home-Automation :
Le patron Command (10/28) Des Commandes (11/28)
Une requête encapsulée
Définition: Command
action()
Le patron Command encapsule une requête comme un execute()
execute()
119 120
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Command : Home-Automation :
Le diagramme de classes du patron (12/28) Notre premier objet Commande (13/28)
L’invoker tient la commande et Interface de tous les commandes.
Le client est responsable de
créer une ConcretCommand à un certain moment demande La commande est invoquée à travers Implémentons l’interface Command
à la commande de réaliser une sa méthode exécute, qui demande
et affecter son Receiver public interface Command{
requête en exécutant sa au Receiver de réaliser des actions
public void execute();
méthode execute() }
Client Invoker
<<interface>> Implémentons une Commande pour allumer la lumière
Command
setCommand() Light
execute()
undo() on()
off()
Home-Automation : Home-Automation :
Notre premier objet Commande (14/28) Notre premier objet Commande (15/28)
Utilisons l’objet Commande Testons la fonctionnalité du contrôleur distant
123 124
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Home-Automation : Home-Automation :
2ème Commande (16/28) Une autre Commande (17/28)
Développer la classe GarageDoorOpenCommand Ajoutant cette commande au slot du contrôleur distant
GarageDoor public class RemoteControlTest{
public static void main(String argv[]) {
up()
SimpleRemoteControl remote = new SimpleRemoteControl();
down()
Light light = new Light();
stop()
LightOnCommand lightOn=new LightOnCommand(light);
lightOn()
lightOff()
GarageDoor garageDoor = new GarageDoor();
GarageDoorOpenCommand garageOpen = new
public class GarageDoorOpenCommand implements Command { GarageDoorOpenCommand(garageDoor);
GarageDoor garageDoor;
public GarageDoorOpenCommand (GarageDoor garageDoor){ remote.setCommand(lightOn); //encapsuler la commande
this.garageDoor = garageDoor; remote.buttonWasPressed(); //allumer la lumière
}
public void execute(){ remote.setCommand(garageOpen); //encapsuler la commande
garageDoor.up(); remote.buttonWasPressed(); //ouvrir la porte du garage
garageDoor.lightOn();
} }
}
125 126
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Home-Automation : Home-Automation :
Le contrôleur distant (18/28) Le diagramme de classes (19/28)
execute()
execute() <<interface>>
Le client RemoteControl
Command
Light onCommands execute()
Garage offCommands
Door execute() RemoteLoader
Stereo setCommand()
execute() onButtonWasPushed()
offButtonWasPushed()
execute()
execute()
Light LightOnCommand
Les actions de la méthode execute() on()
sont invoquées sur le Receiver execute()
off()
LightOffCommand
L’invoker Contrôleur distant execute() public void execute(){
off() light.on();
on() public void execute(){
}
127 128 light.off();
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] } Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Home-Automation : Home-Automation :
Programmer le contrôleur distant (20/28) Programmer les commandes (21/28)
class RemoteControl{
Command onCommands[];
Command offCommands[];
Programmer les classes suivantes:
public RemoteControl() {
onCommands = new Command[7]; Light.java, Stereo.java
offCommands = new Command[7];
Command noCommand = new NoCommand(); Eviter la gestion de null LightOnCommand,.java LightOffCommand.java,
for(int i=0;i<7;i++){
onCommands[i] = noCommand; StereoOnWithCDCommand.java, StereoOffCommand.java
offCommands[i] = noCommand;
} } class LightOnCommand class StereoOnWithCDCommand
public void setCommand(int slot, Command onCommand,Command offCommand ){ implements Command {
onCommands[slot] = onCommand; implements Command {
Light light; Stereo stereo;
offCommands[slot] = offCommand;
} public LightOnCommand(Light public StereoOnWithCDCommand(Stereo
public void onButtonWasPressed(int slot){ light){ stereo){
onCommands[slot].execute(); this.stereo = stereo;
} this.light = light;
public void offButtonWasPressed(int slot){ } }
offCommands[slot].execute(); public void execute(){ public void execute(){
} light.on(); stereo.on();
public String toString(){ stereo.setCD();
String s=""; }
for(int i=0;i<7;i++){ } stereo.setVolume(11);
s+="Slot["+i+"] "+onCommands[i].getClass().getName() +" }
"+offCommands[i].getClass().getName()+"\n"; }
} return s;
}}
129 130
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Home-Automation : Home-Automation :
Tester le contrôleur (22/28) Undo: Annuler la dernière opération (23/28)
class RemoteLoader{ Implémentons l’interface Command
public static void main(String arg[]){
public interface Command{
RemoteControl remoteControl = new RemoteControl();
public void execute();
Light livingRoomLight=new Light();
public void undo();
LightOnCommand livingRoomLightOnCommand = new
}
LightOnCommand(livingRoomLight);
LightOffCommand livingRoomLightOffCommand = new
Implémentons une Commande pour allumer la lumière
LightOffCommand(livingRoomLight);
remoteControl.setCommand(0, livingRoomLightOnCommand,livingRoomLightOffCommand); public class LightOnCommand implements Command { Light
Light light;
remoteControl.onButtonWasPressed(0); on()
public LightOnCommand(Light light){
remoteControl.offButtonWasPressed(0); off()
Stereo stereo = new Stereo(); this.light = light;
StereoOnWithCDCommand stereoOnWithCDCommand = new }
StereoOnWithCDCommand(stereo); public void execute(){
StereoOffCommand stereoOffCommand = new StereoOffCommand(stereo); light.on();
remoteControl.setCommand(1,stereoOnWithCDCommand,stereoOffCommand); }
remoteControl.onButtonWasPressed(1); public void undo(){ Opération à exécuter en cas
remoteControl.offButtonWasPressed(1); light.off(); d’annulation de cette commande
System.out.println(remoteControl.toString()); }
} }
}
131 132
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Home-Automation : Home-Automation :
Undo: Annuler la dernière opération (24/28) Tester le contrôleur avec UNDO (25/28)
class RemoteControl{
Command onCommands[]; Annuler l’allumage de la lumière
Command offCommands[];
Command undoCommand; C’est ou on stockera la dernière class RemoteLoader{
public RemoteControl() { commande exécutée pour le bouton undo public static void main(String arg[]){
onCommands = new Command[7]; //…
offCommands = new Command[7]; remoteControl.onButtonWasPressed(0);
Command noCommand = new NoCommand(); remoteControl.undoButtonWasPressed();
for(int i=0;i<7;i++){
onCommands[i] = noCommand;
offCommands[i] = noCommand; //…
} Initialisation à NoCommand afin
undoCommand = noCommand; } remoteControl.onButtonWasPressed(1);
d’éviter le traitement de null
public void onButtonWasPressed(int slot){ remoteControl.offButtonWasPressed(1);
onCommands[slot].execute(); System.out.println(remoteControl.toString());
undoCommand = onCommands[slot]; }
} }
public void offButtonWasPressed(int slot){ Enregistrer la dernière commande
offCommands[slot].execute();
undoCommand = offCommands[slot];
}
public void undoButtonWasPressed(){ Lorsqu’on appuie sur le bouton undo, on
undoCommand.undo(); invoque la méthode undo() pour annuler
} la dernière commande exécutée
}² 133 134
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Home-Automation : Home-Automation :
La Macro-Commande (26/28) MacroCommand (27/28)
C’est le regroupement de plusieurs commandes en une seule Créer les macro-commandes
public class MacroCommand implements Command { Stocker les commandes
Command [] commands; Command [] on = {lightOnCommand, stereoOnWithCDCommand, tvOnCommand};
de la macro-commande
public MacroCommand (Command [] commands){ Command [] off = {lightOffCommand, stereoOffWithCDCommand, tvOffCommand};
this.commands = commands;
} Un boucle pour exécuter Les commandes sous
public void execute(){ toutes les commandes de la MacroCommand onMacro= new MacroCommand(on); formes de tableau
for (int i=0;i<commands.length;i++) macro-commande MacroCommand offMacro= new MacroCommand(off);
commands[i].execute();
} Affecter les macro-commandes à un bouton
}
remoteControl.setCommand(2, onMacro, offMacro);
Créer les commandes à mettre dans la macro-commande
Affecter les macro-commandes
LightOnCommand lightOnCommand = new LightOnCommand(light);
StereoOnWithCDCommand stereoOnWithCDCommand = new
Exécuter la macro-commande au bouton du slot n°2
StereoOnWithCDCommand(stereo); System.out.println("Macro On");
TVOnCommand tvOnCommand = new TVOnCommand(tv); remoteControl.onButtonWasPushed(2);
Créer les commandes System.out.println("Macro Off"); Tester ces macro-commandes et
//créer aussi les Off-Commandes remoteControl.offButtonWasPushed(2); donner le résultat de l’exécution
135 136
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
Autre utilisation du patron Command :
Organiser les queues de requêtes (28/28) Récapitulatif (1/2)
Les commandes nous offrent une manière de execute()
Bases de l’OO: Abstraction, Encapsulation, Polymorphisme & Héritage
paquetage les morceaux de calcul (computation). Principes de l’OO
Les calculs (commandes créées par des applications) Encapsuler ce qui varie
execute()
seront placés dans des queues (queue de jobs) pour Favoriser la composition sur l’héritage
être exécutés. execute() Programmer pour des interfaces
Opter pour une conception faiblement couplée
execute()
Les classes doivent être ouvertes pour les extensions et fermées pour les
modifications
Dépendre des abstractions. Ne jamais dépendre de classes concrètes
C’est une manière efficace Patron de l’OO
pour limiter le calcul à un Strategy: définit une famille d’algorithmes interchangeables
nombre fixé de Threads
execute()
Observer: définit une dépendance1-à-plusieurs entre objets.
Les threads récupèrent les commandes Decorator: attache des responsabilités additionnelles à un objet dynamiquement.
de la queue une à une, et appellent leur Abstract Factory: offre une interface de création de familles d’objets
méthode execute(). Une fois complété, Factory Method: définit une interface de création des objets
execute() execute()
ils retournent pour une nouvelle
commande. Singleton: assure à une classe une seule instance
Command: encapsule une requête comme un objet
137 138
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
1. Donne le digramme de classes décrivant cette transformation avec le pattern
Command.
2. Implanter ce diagramme tout en respectant le client suivant :
Le Modèle-Vue-
public class Client {
public static void main(String[] args) {
Contrôleur
Calculatrice c=new Calculatrice();
PlusCommand plus =new PlusCommand(c);
MultipCommand mult=new MultipCommand (c);
SoustCommand sous =new SoustCommand(c);
CalculatriceControl control =new CalculatriceControl();
control.setCommand(0, plus);
control.setCommand(1, sous);
control.setCommand(2, mult);
control.MultiButtonPressed(2, 5, 15);
control.SoustButtonPressed(1, 17, 10);
control.PlusButtonPressed(0, 12, 15); }}
141 142
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
143 144
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
La Vue Le Contrôleur
Le contrôleur prend en charge la gestion des événements de
synchronisation pour mettre à jour la vue ou le modèle
Ce avec quoi l'utilisateur interagit se nomme précisément la vue. reçoit tous les événements de l'utilisateur et enclenche les actions à
présenter les résultats renvoyés par le modèle. effectuer
recevoir toute action de l'utilisateur (hover, clic de souris, sélection d'un bouton radio, Si une action nécessite un changement des données, le contrôleur
cochage d'une case, entrée de texte, de mouvements, de voix, etc.). demande la modification des données au modèle, et ce dernier notifie la
Ces différents événements sont envoyés au contrôleur. vue que les données ont changé pour qu'elle se mette à jour.
La vue n'effectue pas de traitement, D'après le patron de conception observateur/observable, la vue
afficher les résultats des traitements effectués par le modèle et interagir avec est un « observateur » du modèle qui est lui « observable »
l'utilisateur. Le contrôleur n'effectue aucun traitement, ne modifie aucune donnée.
Plusieurs vues peuvent afficher des informations partielles ou non d'un même Il analyse la requête du client et se contente d'appeler le modèle adéquat et de
modèle. renvoyer la vue correspondant à la demande.
145 146
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
147 148
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
PizzaStore :
Créer des pizzas (1/37)
Pizzeria: Créer des pizzas
Pour la flexibilité, on veut que
Pizza orderPizza(){ celui-ci soit une classe abstraite ou
Le patron
Pizza pizza=new Pizza(); interface, sauf qu’on ne peut pas
instancier l’un des deux derniers!
pizza.prepare();
"Factory"
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
149 150
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
PizzaStore : PizzaStore :
Plusieurs types de pizza (2/37) Autres types de pizza (3/37)
Les concurrents ont ajouté un nouveau type de pizza: (avec Calamars)
Pizzeria: Créer plusieurs types de pizza
Ajouter ClamPizza au menu
On passe le type du pizza à travers
Pizza orderPizza( String type ){
le méthode orderPizza() On n’a pas vendu beaucoup de GreekPizza dernièrement
Pizza pizza;
Suspendre GreekPizza du menu
if (type.equals("cheese"){ Pizza orderPizza(String type){
pizza=new CheesePizza(); Pizza pizza;
} else if (type.equals("greek"){ Selon le type de pizza, on crée la if (type.equals("cheese"){
pour la modification!
pizza=new GreekPizza(); pizza=new CheesePizza();
} else if (type.equals("pepperoni"){ la variable "pizza" (interface et } else if (type.equals("greek"){ Partie variable: On modifie
pizza=new PepperoniPizza(); classe mère des pizzas). pizza=new GreekPizza(); le code autant que la
} } else if (type.equals("pepperoni"){ sélection de pizza change.
pizza=new PepperoniPizza();
pizza.prepare(); } else if (type.equals("clam"){
pizza.bake(); Une fois on a la pizza, on prépare la sauce, le pizza=new ClamPizza();
pizza.cut(); nappage (tomate/crême fraiche) et le fromage , }
pizza.box(); puis on la fait cuire, la coupe, et on la met dans pizza.prepare();
return pizza; une boite. pizza.bake(); Partie invariable: Généralement, ces opérations
} pizza.cut(); sont les mêmes pour des années et des années.
pizza.box();
return pizza;
151 152 }
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
PizzaStore : PizzaStore :
Encapsuler la création (4/37) Un Simple Factory (5/37)
Règle 1 de l’OO: Encapsuler ce qui varie Le seul rôle de SimplePizzaFactory est de créer des pizzas pour ces
if (type.equals("cheese"){
clients
pizza=new CheesePizza(); Initialement, on définit la méthode
} else if (type.equals("pepperoni"){ createPizza() dans le Factory. C’est la
pizza=new PepperoniPizza();
public class SimplePizzaFactory { méthode que tous les clients utilisent
} else if (type.equals("clam"){
public Pizza createPizza(String type){ pour instancier des nouveaux objets.
Pizza pizza=null;
pizza=new ClamPizza();
}
Pizza orderPizza(String type){ if (type.equals("cheese"){
Pizza pizza; pizza=new CheesePizza();
} else if (type.equals("pepperoni"){
On place ce code dans un objet qui s’occupera de la pizza=new PepperoniPizza();
création des pizzas concrètes. Si un autre objet a besoin } else if (type.equals("clam"){
d’une pizza concrète, c’est cet objet qu’il faut appeler. pizza=new ClamPizza();
pizza.prepare(); } C’est le code qu’on a retirer de la
pizza.bake(); méthode orderPizza().
pizza.cut(); return pizza;
pizza.box(); }
return pizza; }
}
153 OnRiadh
attribue le nom Factory à ce nouvel objet 154
BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
PizzaStore : PizzaStore :
Retravaillons la classe PizzaStore (6/37) Le diagramme de classes (7/37)
Maintenant, PizzaStore utilise SimplePizzaFactory pour créer des pizzas Elle doit être la seule partie de On définit Pizza comme
notre application qui pointe vers les classe abstraite, avec quelque
PizzaStore récupère la classes des pizzas concrètes implémentations qui peuvent
public class PizzaStore {
référence du factory à être redéfinies
SimplePizzaFactory factory;
travers le constructeur.
public PizzaStore(SimplePizzaFactory factory){ PizzaStore SimplePizzaFactory Pizza
this.factory=factory;
} prepare()
orderPizza() createPizza() bake()
public Pizza orderPizza(String type){
Pizza pizza; cut()
box()
La méthode de création
pizza=factory.createPizza(type);
est souvent statique
pizza.prepare();
pizza.bake(); La méthode orderPizza() utilise le CheesePizza ClamPizza
PizzaStore crée des PepperoniPizza
pizza.cut(); factory pour créer ses pizzas. instances à travers
pizza.box();
return pizza; SimplePizzaFactory
}
} Chaque produit implémente la classe abstraite Pizza
Noter qu’on a remplacé l’opérateur new par une méthode concrète Actuellement, Simple Factory n’est pas un patron. C’est plutôt un
Plus d’instanciation concrète ici "style" de programmation
155 156
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
PizzaStore : PizzaStore :
Franchise de PizzaStore (8/37) Différent styles des PizzaStores (9/37)
Objectif: Franchiser PizzaStore: Créer plusieurs stores dans Si on prépare les mêmes pizzas Créer des pizzas à travers
SfaxPizzaFactory
plusieurs villes (Sfax, Tunis, etc.)
SfaxPizzaFactory sffactory = new SfaxPizzaFactory();
Besoin: Développer une application qui gère la création des PizzaStore sfstore = new PizzaStore(sffactory);
pizzas pour chaque store sfstore.orderPizza("cheese"); Créer les mêmes pizzas à
travers TunisPizzaFactory
Chaque store offre différent types de pizza
Conception: OO TunisPizzaFactory tnfactory = new TunisPizzaFactory();
PizzaStore tnstore = new PizzaStore(tnfactory);
tnstore.orderPizza("cheese");
Moins de fromage dans
les pizzas, etc.
Et si chaque PizzaStore prépare ses propres styles de pizza
Je prépare les pizzas depuis des
années et je veux ajouter mes propres
Des pizzas familles de touches d’amélioration des procédures de
grande taille, beaucoup mon PizzaStore
de fromage, etc.
157 158
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
PizzaStore : PizzaStore :
Le framework de PizzaStore (10/37) Les styles de pizzas (11/37)
On donne la liberté aux franchises de créer leurs propres styles PizzaStore Méthode abstraite, à définir
dans les classes dérivées
Déplacer la création dans une la méthode createPizza() et garder la createPizza()
orderPizza()
même méthode orderPizza() pour tous les stores
Chaque région l’étend afin de spécifier son propre style
Créer des pizzas
public abstract class PizzaStore { Créer des pizzas SfaxStylePizzaStore TunisStylePizzaStore avec les ingrédients
avec les ingrédients spécifiques à Tunis
spécifiques à Sfax createPizza() createPizza()
public Pizza orderPizza(String type){
Pizza pizza; CreatePizza() est une
méthode PizzaStore plutôt
Pizza createPizza(String type) { Pizza createPizza(String type) {
pizza = createPizza(type); que dans le Factory
Pizza pizza=null; Pizza pizza=null;
pizza.prepare(); if (type.equals("cheese"){ if (type.equals("cheese"){
pizza.bake(); Tout ceci apparaît le même pizza=new SfaxStyleCheesePizza(); pizza=new TunisStyleCheesePizza();
} else if (type.equals("pepperoni"){ } else if (type.equals("pepperoni"){
pizza.cut(); On a transféré notre objet pizza=new SfaxStylePepperoniPizza(); pizza=new TunisStylePepperoniPizza();
pizza.box(); Factory dans cette méthode } else if (type.equals("clam"){ } else if (type.equals("clam"){
return pizza; pizza=new SfaxStyleClamPizza(); pizza=new TunisStyleClamPizza();
} } }
abstract Pizza createPizza(String type); return pizza; return pizza;
}
} }
Notre "méthode Factory" est
159 160
maintenant abstraite dans
Riadh PizzaStore
BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
PizzaStore : PizzaStore :
Un PizzaStore de Style Sfaxien (12/37) Commander des pizzas (13/37)
Les bénéfices d’une franchise Je voudrai une pizza Je voudrai une pizza
On obtient des fonctionnalités des pizzas communes (prepare(), bake(), cut() et box()) de grande taille avec beaucoup de taille moyenne avec
Chaque région définit sa propre méthode createPizza() qui spécifie son style de pizza de fromage du style peu de fromage et au thon
tunisien du style sfaxien
public class SfaxStylePizzaStore extends PizzaStore {
Pizza createPizza(String item) { On hérite la méthode
if (item.equals("cheese"){ orderPizza() de PizzaStore PizzaStore tunisps = PizzaStore sfaxps =
return new SfaxStyleCheesePizza(); new TunisStylePizzaStore(); new SfaxStylePizzaStore();
} else if (item.equals("pepperoni"){
On implémente createPizza()
return new SfaxStylePepperoniPizza();
puisqu’elle est abstraite: Instance du store spécifique
} else if (item.equals("clam"){
C’est la "méthode Factory"
return new SfaxStyleClamPizza();
}
} Créer des pizzas du style sfaxien!!
} Prendre des commandes
Rq: Toutes les responsabilités d’instanciation sont déplacées vers la méthode
createPizza() qui agit comme un factory tunisps.orderPizza("cheese"); sfaxps.orderPizza("cheese");
La méthode factory gère la création des objets et leur encapsulation
Découplage du code du client dans la supère classe de la création de l’objet dans les sous- C’est une méthode de l’instance tunisps (respectivement
classes sfaxps), définie dans la classe PizzaStore
161 162
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
PizzaStore : PizzaStore :
Commander des pizzas (14/37) Implémenter (15/37) Les créateurs
PizzaStore
tunisps.orderPizza("cheese"); sfaxps.orderPizza("cheese"); Implémenter les classes: createPizza()
PizzaStore.java, SfaxPizzaStore.java orderPizza()
Factory Method
La méthode orderPizza() appelle createPizza() et TunisPizzaStore.java
SfaxStylePizzaStore TunisStylePizzaStore
createPizza() createPizza()
Pizza pizza= createPizza("cheese"); Pizza pizza= createPizza("cheese");
La méthode createPizza() est implémentée dans la classe dérivée Implémenter les classes: Pizza.java, Les produits
Elle retourne une pizza au Elle retourne une pizza au SfaxStyleCheesePizza.java, Pizza
fromage style tunisien fromage style sfaxien
SfaxStyleClamPizza.java,
On termine la préparation SfaxStylePepperoniPizza.java,
pizza.prepare(); pizza.prepare(); TunisStyleCheesePizza.java,
pizza.bake(); pizza.bake(); TunisStyleClamPizza.java, SfaxStyleCheesePizza TunisStyleCheesePizza
pizza.cut(); pizza.cut(); TunisStylePepperoniPizza.java SfaxStyleClamPizza TunisStyleClamPizza
pizza.box(); pizza.box();
SfaxStylePepperoniPizza
TunisStylePepperoniPizza
163 164
De style tunisien De style sfaxien
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
PizzaStore : PizzaStore :
Le patron Factory Method (16/37) Le diagramme de classes du patron (17/37)
La classe mère de tous les produits Le Creator est une classe qui contient
Le ConcretCreator
implémente la méthode
facotoryMethod(), qui
ConcretProduct ConctretCreator produit les produits
factoryMethod()
La décision: choix
Le ConcretCreator est responsable de la création de produits du produit concret
concrets. C’est la classe qui connait le qui a créé chaque produit
165 166
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
PizzaStore : PizzaStore :
Un PizzaStore dépendant (18/37) La dépendance entre les objets (19/37)
Hypothèse: On n’a jamais entendu parler du factory Cette version de PizzaStore dépend de tous les objets pizzas parce qu’elle les crée
Compter le nombre d’objets de pizzas concrètes dont cette classe dépend directement
Refaire le compte si on ajoute des pizzas de style bizertin On dit que PizzaStore dépend des implémentations des pizzas parce que chaque
public class DependentPizzaStore { changement des implémentations concrètes des pizzas, affecte le PizzaStore
Pizza createPizza(String style, String type) {
Pizza pizza=null; Si les implémentations des pizzas
if (style.equals("Sfax")){ Gérer toutes les pizzas de
changent, on doit modifier PizzaStore
if (type.equals("cheese"){
pizza=new SfaxStyleCheesePizza();
style sfaxien
} else if (type.equals("pepperoni"){
pizza=new SfaxStylePepperoniPizza();
} else if (type.equals("clam"){
pizza=new SfaxStyleClamPizza();
}
} else if (style.equals("Tunis")){
if (type.equals("cheese"){ Gérer toutes les pizzas de
pizza=new TunisStyleCheesePizza(); style tunisois
} else if (type.equals("pepperoni"){
pizza=new TunisStylePepperoniPizza();
} else if (type.equals("clam"){
pizza=new TunisStyleClamPizza();
}
} else {System.out.println("Erreur: type de pizza invalide");}
pizza.prepare();
pizza.bake(); Pour chaque nouveau type de pizza,
pizza.cut(); on ajoute une autre dépendance dans
pizza.box();
return pizza; la méthode create()
167 } 168
} Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
PizzaStore : PizzaStore :
L’inversion de dépendance (20/37) Appliquons ce principe (21/37)
Réduire les dépendances aux classes concrètes dans notre code, PizzaStore dépend seulement de
Pizza, qui est une classe abstraite
est une "bonne chose" Pizza est une classe
abstraite.. abstraction
Le principe qui formalise cette notion s’appelle "principe Les classes de pizzas concrètes dépendent
d’inversion de dépendance" : aussi de l’abstraction Pizza, parce qu’elles
implémentent l’interface Pizza
Règle 5: Dépendre des abstractions. Ne jamais dépendre
de classes concrètes.
Ce principe prétend que nos "haut-niveau" composants ne
doivent pas dépendre de nos "bas-niveau" composants; plutôt, les
deux doivent dépendre des abstractions.
Le "Factory Method"
Un composant de haut-niveau (PizzaStore) est une classe dont le est la technique la plus
comportement dépend des autres composants de bas- puissante d’adhérence
niveau(Pizza) au principe d’inversion
de dépendance, mais
pas la seule...
169 170
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
PizzaStore : PizzaStore :
Les ingrédients des pizzas (22/37) Les menus des pizzas (23/37)
Problème : quelques franchises n’ont pas utilisé la
même procédure de préparation, et ce en substituant
Sfax Tunis
des ingrédients par d’autres de basse qualité, afin Pizza Menu
Pizza Menu
d’augmenter leur marge. Cheese Pizza
Nous avons les mêmes
familles de produits, mais Cheese Pizza
! Il faut assurer la consistance des ingrédients Sauce marinara, Parmesan,
Emmental différente implémentations Sauce tomate prune, Mozzarella,
Roquefort
selon la région
Solution : créer un factory qui produit les ingrédients, Clam Pizza
Sauce marinara, Parmesan,
Clam Pizza
Sauce tomate prune, Mozzarella,
et les transporter aux franchises Clovis, Olive verte Palourde, Olive noire
Pepperoni Pizza
Le seul problème avec ce plan : Ce qui est sauce rouge Sauce marinara, Parmesan,
Pepperoni Pizza
Sauce tomate prune, Mozzarella,
Aubergine, Poivron, Olive verte
à Sfax, n’est pas sauce rouge à Tunis Épinard, Poivre, Olive noire
PizzaStore : PizzaStore :
Les factories de Sfax (26/37) Retravaillons la classe Pizza (27/37)
public class SfaxPizzaIngredientFactory implements PizzaIngredientFactory public abstract class Pizza { Les ingrédients d’une paizza
{ String name; (liste non-exhaustive)
public Dough createDough(){ Dough dough;
return new ThinDough(); Pour chaque famille d’ingrédient, on Sauce sauce;
La collecte des ingrédients se fait dans cette
} crée la version sfaxienne Cheese cheese;
méthode (à travers un factory d’ingrédients)
public Sauce createSauce(){ Veggies veggies[];
qui sera définie par les classes dérivées
return new MarinaraSauce(); Clam clam;
} abstract void prepare();
public Cheese createCheese(){ void bake(){
return new Parmesan(); System.out.println("Cuire durant 25mn à 350°");}
} void cut(){
public Veggies[] createVeggies(){ System.out.println("Couper en morceaux à la diagonale");}
Veggies veggies[]={new Garlic(), new Onion(), new Eggplant()}; void box(){
return veggies; System.out.println("Placer la pizza dans un boitier officiel");}
} void setName(String s){
public Clam createClam(){ name=s;}
return new Clovis(); String getName(){ Les autres méthodes sont les
} Palourde() pour le return name;} mêmes (à l’exception de prepare())
} cas de tunis }
175 176
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
PizzaStore : PizzaStore :
Retravaillons les classes des Pizzas (28/37) Retravaillons les classes des Pizzas (29/37)
Pour faire la pizza, on besoin d’un
factory. Chaque classe Pizza prend le
factory à travers son constructeur Un factory pour chaque type de Pizza
public class CheesePizza extends Pizza { public class ClamPizza extends Pizza {
PizzaIngredientFactory ingredientfactory; PizzaIngredientFactory ingredientfactory;
CheesePizza(PizzaIngredientFactory ingredientfactory){ ClamPizza(PizzaIngredientFactory ingredientfactory){
this.ingredientfactory = ingredientfactory; this.ingredientfactory = ingredientfactory;
} }
void prepare(){ void prepare(){
System.out.println("Préparons " +name); System.out.println("Préparons " +name);
dough = ingredientfactory.createDough(); dough = ingredientfactory.createDough();
sauce = ingredientfactory.createSauce(); sauce = ingredientfactory.createSauce();
cheese = ingredientfactory.createCheese(); cheese = ingredientfactory.createCheese();
} clam= ingredientfactory.createClam();
} Pour faire une ClamPizza, la méthode
} Chaque fois que la méthode prepare() prépare les ingrédients
a besoin d’ingrédient, elle appelle le } correspondants de son factory local.
factory pour le produire Si c’est le factory de sfax, on
va préparer des clovis
177 178
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
PizzaStore : PizzaStore :
Retravaillons les PizzaStores (30/37) Factories (31/37)
Le store de Sfax est composé d’un Définit l’interface
factory sfaxien d’ingrédients/
public class SfaxPizzaStore extends PizzaStore {
Pizza createPizza(String item) { Offre les
Abstract Ingredient Factory
Pizza pizza = null; implémentations
PizzaIngredientFactory ingredientfactory= des ingrédients
new SfaxPizzaIngredientFactory();
On passe à chaque pizza le factory
if (item.equals("cheese"){ censé créer ses ingrédients
pizza = new CheesePizza(ingredientfactory);
pizza.setName("Sfax Style Cheese Pizza"); Sfax Tunis
} else if (item.equals("pepperoni"){
pizza = new PepperoniPizza(ingredientfactory);
pizza.setName("Sfax Style Pepperoni Pizza"); De l’abstract factory, on dérive
} else if (item.equals("clam"){ plusieurs concrets factories qui
pizza = new ClamPizza(ingredientfactory); produisent les mêmes produits, mais avec
pizza.setName("Sfax Style Clam Pizza"); différentes implémentations
} En passant (au constructeur) une variété
return pizza; de factories, on obtient une variété
} d’implémentations, tout en gardant le
} même code du client
179 180
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
PizzaStore : PizzaStore :
Commander des pizzas (32/37) Commander des pizzas (33/37)
tunisps.orderPizza("cheese"); sfaxps.orderPizza("cheese");
Je voudrai une pizza Je voudrai une pizza
de grande taille avec beaucoup de taille moyenne avec
de fromage du style La méthode orderPizza() appelle initialement createPizza()
peu de fromage et au thon
tunisien du style sfaxien
Pizza pizza= createPizza("cheese"); Pizza pizza= createPizza("cheese");
PizzaStore tunisps = PizzaStore sfaxps =
La méthode createPizza() implique le factory d’ingrédients
new TunisPizzaStore(); new SfaxPizzaStore();
Pizza pizza= new Pizza pizza= new
Instance du store spécifique CheesePizza(tunisIngeredientFactory); CheesePizza(sfaxIngeredientFactory);
Prendre des commandes La méthode prepare() est appelée et chaque factory est appelé pour produire les
ingrédients de la région
tunisps.orderPizza("cheese"); sfaxps.orderPizza("cheese");
PizzaStore : PizzaStore :
Commander des pizzas (34/37) Le patron Abstract Factory (35/37)
void prepare(){ void prepare(){
dough = factory.createDough();
// Pâte coustillante
dough = factory.createDough();
// Pâte mince
Définition: Abstract Factory
sauce = factory.createSauce(); sauce = factory.createSauce(); Le patron abstract factory offre une interface de
// Sauce tomate prune // Sauce marinara
cheese = factory.createCheese(); cheese = factory.createCheese(); création de familles d’objets dépendants (en relation), sans
// Mozzarella, Roquefort // Parmesan, Emmental
} }
spécifier leurs classes concrètes.
Elle prépare une pizza au Elle prépare une pizza au
fromage avec les ingrédients fromage avec les ingrédients
du style tunisien du style sfaxien
On termine la création
pizza.bake(); pizza.bake();
pizza.cut(); pizza.cut();
pizza.box(); pizza.box();
183 184
De style tunisien De style sfaxien
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN HALIMA&Wajdi LOUATI [Design patterns]
PizzaStore : PizzaStore :
Le diagramme de classes du patron (36/37) La conception finale (37/37) Les clients de l’abstract factory
sont les stores de Sfax et de Tunis
Le client est composé au moment de
l’exécution par un factory concret SfaxPizzaStore
L’abstract factory définit un Client L’abstract factory définit un <<interface>>
ensemble de méthodes pour la l’ensemble des produits qu’on a Clam
production des produits Une famille de produits besoin pour faire une pizza
<<interface>>
AbstractProductA Clovis Palourde
<<interface>> <<interface>>
AbstractFactory PizzaIngredientFactory
createSauce() <<interface>>
createProductA() createCheese() Sauce
createProductB() createClam()
ProductA2 ProductA1 //etc.
MarinaraSauce PlumTomatoSauce
<<interface>>
AbstractProductB
TunisPizzaIngred SfaxPizzaIngredi <<interface>>
ConcretFactory1 ConcretFactory2 ientFActory entFactory Cheese
createProductA() createProductA()
createSauce() createSauce()
createCheese() createCheese()
createProductB() createProductB() ProductB2 ProductB1 createClam() createClam()
Parmesan Mozzarella
Les factories concrets implémentent les La tâche des factories est de produire les
différences familles de produits ingrédients spécifiques à chaque région
185 186 Chaque factory produit différent implémentation
Riadh BEN HALIMA&Wajdi LOUATI [Design patterns] Riadh BEN
de chaque HALIMA&Wajdi
famille de produits LOUATI [Design patterns]