Vous êtes sur la page 1sur 217

Les patrons de conception

(Design patterns)

Dr. Ing. Riadh BEN HALIMA


Maître-assistant à l’ENIS
riadh.benhalima@enis.tn

1
Plan

 Historique & Motivation


 Le patron « Strategy »
 Le patron « Observer »
 Le patron « Decorator »
 Le patron « Factory »
 Le patron « Singleton »
 Le patron « Command »
 Le patron « Adapter »
 Le patron « Façade »
 Autres patrons

2
Historique
 Notion de « patron » d’abord apparue en architecture :
 l’architecture des bâtiments
 la conception des villes et de leur environnement
 L’architecte Christopher Alexander le définit comme suit:
 «Chaque modèle [patron] décrit un problème qui se manifeste constamment
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:
The Timeless Way of Building, Oxford University Press 1979]

 Projeter la notion de patron à du logiciel : "design pattern"


 premiers patrons à partir de 1987 (partie de la thèse de Erich Gamma)
 puis Richard Helm, John Vlissides et Ralph Johnson («Gang of Four, GoF»)
 premier catalogue en 1993 : Elements of Reusable Object-Oriented Software
 Vocabulaire:
 modèles de conception= patrons de conception= motifs de conception= design
patterns
3
Riadh BEN HALIMA [Design patterns]
Motivation
 Pourquoi définir des patrons de conception
 Construire des systèmes plus extensibles, plus robustes au changement
 Capitaliser l’expérience collective des informaticiens
 Réutiliser les solutions qui ont fait leur preuve
 Accélérer le “development process” en fournissant des paradigmes de
développement prouvés et testés
 Faciliter la communication entre les membres de l’équipe en utilisant
des termes bien connus et bien assimilés
 Complémentaire avec les API
 Une API propose des solutions directement utilisables
 Un patron explique comment structurer son application avec une API
 Patron de conception dans le cycle de développement
 Intervient en conception détaillée
 Reste indépendant du langage d’implantation
4
Riadh BEN HALIMA [Design patterns]
Définition
 Définition : "Un patron de conception (design pattern)
décrit une structure commune et répétitive de
composants en interaction (la solution) qui résout un
problème de conception dans un contexte particulier"
 Au lieu de la réutilisation de code, on parle de la
réutilisation de l’expérience avec les patrons
 Un bon patron de conception :
 résout un problème
 correspond à une solution éprouvée
 favorise la réutilisabilité, l’extensibilité, etc.

5
Riadh BEN HALIMA [Design patterns]
Intérêt et utilisation des patrons de
conception
 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
des applications

Les concepts de l’orienté objet tels que


l’abstraction, l’héritage, et le polymorphisme ne
te rendent pas un bon concepteur!
Un concepteur pense à créer une conception
flexible qui est maintenable et fait face aux
changements

6
Riadh BEN HALIMA [Design patterns]
Ce qu’il ne faut pas attendre des patrons
de conception

 Une solution universelle prête à l’emploi


 Une bibliothèque de classes réutilisables
 L’automatisation totale de l’instanciation d’un
patron de conception
 La disparition du facteur humain

7
Riadh BEN HALIMA [Design patterns]
Type des design patterns (1/4)

Creational

Design
Structural
Patterns

Behavioral

8
Riadh BEN HALIMA [Design patterns]
Type des design patterns (2/4)
 Creational Design Patterns

 Traiter avec les mécanismes de création des objets,


essayant de créer les objets de façon adaptée à la
situation

 La forme basique de la création d’objets peut mener à


des soucis de design ou le rendre plus complexe. Les
Creational design patterns résolvent ce problème en
contrôlant la création des objets

9
Riadh BEN HALIMA [Design patterns]
Type des design patterns (3/4)
 Structural Design Patterns
 Ce sont des partterns qui facilitent le design en
identifiant un moyen simple pour mettre en place la
relation entre les entités

 Behavioral Design Patterns


 Ce sont des patterns qui sont concernés par les
algorithmes et l’assignation des responsabilités entre les
objets

10
Riadh BEN HALIMA [Design patterns]
Type des design patterns (4/4)
Creational Patterns Structural Patterns Behavioral Patterns

- Factory Method - Adapter - Command


- Abstract Factory - Decorator - Observer
- Singleton - Façade - Strategy
- Builder - Bridge - Chain of
- Prototype - Composite responsibility
- Flyweight - Interpreter
- Proxy - Iterator
- Mediator
- Memento
- State
- Template Method
- Visitor
11
Riadh BEN HALIMA [Design patterns]
Utilisation des design patterns
Medium Medium
High Medium Low
High Low
Abstract Adapter Decorator Builder Flyweight
Factory

Factory Command Chain of Interpreter


Bridge
Method responsibility

Strategy Prototype Mediator Memento


Façade

Singleton State Visitor


Observer

Composite Template
Iterator
Method
Proxy

12
Riadh BEN HALIMA [Design patterns]
Le patron
"Strategy"

13
Riadh BEN HALIMA [Design patterns]
SimUDuck:
Conception (1/23)
 Objectif: développement d’un jeu de simulation d’un bassin
pour les canards
 Besoin: nager, cancaner, afficher, etc..
 Supporter une large variété de canards
 Conception: OO
 Une supère classe Canard (Duck) dont tous les canards héritent
Duck
quack()
Abstract swim()
display()

Autres types de Ducks


MallardDuck RedHeadDuck

display() display()

14
Riadh BEN HALIMA [Design patterns]
SimUDuck : Innovation (2/23)
 Objectif: Innovation (pour impressionner et vendre +)
 Besoin: simuler le vol des canards!
 Conception: OO
 Ajouter la méthode fly() à la supère classe

Duck
quack()
swim()
Modification apportée fly()
display()

Autres types de Ducks


MallardDuck RedHeadDuck

display() display()

15
Riadh BEN HALIMA [Design patterns]
SimUDuck :
Problèmes (3/23)
 Besoin: Au moment de la démonstration du simulateur, on nous
demande de simuler des canards en caoutchouc
 Conception: OO
 Ajouter la classe RubberDuck qui hérite de la supère classe Duck

Duck
! Le caoutchouc ne vole pas

quack()
swim()
Hériter par tous les canards fly()
display()

MallardDuck RedHeadDuck RubberDuck

display()
display() display()
quack(){
Redéfinir à squeak 
}
16
? Le caoutchouc ne cancane pas
Riadh BEN HALIMA [Design patterns]
SimUDuck :
Constat (4/23)
 Problème 1: Le canard en caoutchouc ne cancane pas!
 Solution : Redéfinir la méthode quack() à squeak()
(résolu)
 Problème 2: Le canard en caoutchouc ne vole pas!
Toutefois, il hérite la méthode fly() de la supère classe
Duck!
 Constat:
 Ce que nous avons cru une utilisation formidable de l’héritage
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!
17
Riadh BEN HALIMA [Design patterns]
SimUDuck :
Solution?? (5/23)
 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 fly() de RubberDuck
RubberDuck
display()
quack(){ squeak }
fly(){
Redéfinir à rien 
}

 Question: est ce que c’est résolu pour tous types de canards?

18
Riadh BEN HALIMA [Design patterns]
SimUDuck :
Un autre canard (6/23)
 Nouveau type de canard: Canard en bois
 Problèmes levés:
 Ce canard ne cancane pas
 Ce canard ne vole pas
 Solution: redéfinir (une autre fois) les méthodes quack() et fly()
WoodenDuck
display()
quack(){
Redéfinir à rien 
}
fly(){
Redéfinir à rien 
}
 Inconvénients de l’utilisation de l’héritage
 Il est difficile de connaitre le comportement de tous les canards
 Un changement non-intentionnel, affecte les autres canards
19
Riadh BEN HALIMA [Design patterns]
SimUDuck :
Des interfaces? (7/23)
 Hypothèse: On nous demande de mettre à jour SimUDuck tous les 6 mois: La
spécification demeure changeante
 Vérifier fly() et quack() pour chaque nouveau canard
 Ré-écrire (si besoin) fly() et quack()
 Solution possible pour contourner le problème: les interfaces
Interface
Duck
Flyable Quackable swim()
fly() quack() display()

MallardDuck RedHeadDuck RubberDuck WoodenDuck


display() display() display() display()
fly() fly() quack()
quack() quack()

20 Que dites vous de cette conception ?


Riadh BEN HALIMA [Design patterns]
SimUDuck :
Inconvénients (8/23)
Duck
Flyable Quackable swim()
fly() quack() display()

MallardDuck RedHeadDuck RubberDuck WoodenDuck


display() display() display() display()
fly() fly() quack()
quack() quack()

 Constat:
 Duplication de code: méthodes fly() et quack() dans les sous-classes
 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)
 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 +)
21
Riadh BEN HALIMA [Design patterns]
SimUDuck :
Moment de réflexion (9/23)
 Pas toutes les sous-classes qui ont besoin de voler (fly)
ou de cancaner (quack)
 L’héritage n’est pas la bonne solution
 Les interfaces Flyable et Quackable résolvent une
partie du problème
 Détruit complètement la réutilisation du code pour ces
comportements
 La maintenance et la mise à jour représentent un vrai calvaire
 Supposant qu’il existe plus qu’une façon de voler
 Maintenance plus difficile…

22
Riadh BEN HALIMA [Design patterns]
SimUDuck :
Solution (10/23)

 Solution:
 Design pattern : solution ultime, cheval blanc, sauveur…

 Trouvons une solution avec l’"ancienne-mode" et ce en


applicant les bonnes principes de la conception OO
 Concevoir une application, ou un besoin de
modification/changement peut être appliqué avec le
moindre possible d’impact sur le code existant
Donner des raisons de changement de code
dans votre application
23
Riadh BEN HALIMA [Design patterns]
SimUDuck :
Principe de conception (11/23)

Règle 1: Identifier les aspects variables de mon


application et les séparer de ce qui reste invariant

C’estla base de tous les patrons de conception


Système plus flexible+peu de conséquences inattendues

Mise en œuvre
Prendre la partie qui varie et l’encapsuler. De cette
façon, un changement ultérieur affecte la partie
variable, sans toucher à celle invariable
24
Riadh BEN HALIMA [Design patterns]
SimUDuck :
Séparation (12/23)
 La classe Duck est toujours la supère classe
 Les comportements fly() et quack() sont retirés, et mis dans une
autre structure

Comportements
Mettre dehors ce qui varie

Duck class Le comportement Fly

Le comportement Quack

Invariable Variable
25
Riadh BEN HALIMA [Design patterns]
SimUDuck :
Conception des comportements (13/23)
 Conception initiale: l’inflexibilité des comportements a engendré
des troubles
 On veut affecter les comportements aux instances des Ducks
tout en permettant:
 La création d’une instance (MallardDuck),
 L’initialisation avec un type de comportement (type de vol)
 La possibilité de changer le type de vol dynamiquement (?)

 Règle 2: Programmer des interfaces, et non des


implémentations (Program to Interface)
 Programmer pour les super-types!
 On utilise des interfaces pour représenter chaque
comportement: FlyBehavior et QuackBehavior
26
Riadh BEN HALIMA [Design patterns]
SimUDuck :
Conception des comportements (14/23)
FlyBehavior QuackBehavior
fly() quack()

FlyWithWings FlyNoWay Quack Squeak MuteQuack


fly() { fly() { quack(){ quack(){ quack(){
//vol //rien-ne vol pas //quack //squeak //rien-ne
} } } //rubber //cancane pas
} }
 Conséquences:
 On peut ajouter un nouveau comportement sans modifier ni le code des
comportements existants, ni le code des classes des canards qui utilisent les
comportements voler/cancaner
 Avec cette conception, d’autres objets peuvent réutiliser le comportement fly et
quack, parce qu’ils ne sont plus cachés dans les classes canards.

27
Riadh BEN HALIMA [Design patterns]
SimUDuck :
Intégration des comportements(15/23)
 La supère classe Duck, dont hérite tous les canards
Duck Variable d’instance
du type INTERFACE
FlyBehavior fbehavior;
QuackBehavior qbehavior;
performQuack()
swim()
display()
performFly()
//….

Ces méthodes remplacent quack() et fly()

 La clé: le canard délègue les comportements fly et quack,


au lieu d’utiliser les méthodes fly() et quack() définies
dans la supère classe Duck.
28
Riadh BEN HALIMA [Design patterns]
SimUDuck :
Implémentation de la supère classe(16/23)
 La supère classe Duck, dont hérite tous les canards

public abstract class Duck{


QuackBehavior qbehavior; Chaque type de canard initialise ces attributs
FlyBehavior fbehavior; selon ses besoins.
//… (par FlyWithWings pour le MallardDuck )
public void performQuack(){
qbehavior.quack();
}
public void performFly(){
fbehavior.fly(); Grace au polymorphisme, la bonne méthode sera
} invoquée dans la sous-classe du type de canard.
(Déléguée à la classe gérant le comportement)
//..
}

29
Riadh BEN HALIMA [Design patterns]
SimUDuck :
Implémentation d’un canard (17/23)

Cette classe inclut les méthodes réalisant


le comportement fly et quack, par héritage
(performQuack(), etc..)

public class MallardDuck extend Duck{


public MallardDuck (){ Initialisation des attributs déclarés
fbehavior = new FlyWithWings(); dans la supère classe Duck
qbehavior = new Quack();
}
public void display(){
System.out.println("Je suis un canard Mallard");
}
}

30
Riadh BEN HALIMA [Design patterns]
SimUDuck :
Tester le code du canard(18/23)
 Développer et compiler [Utiliser NetBeans/Eclipse]:
 La classe abstraite Duck (Duck.java)
 Le comportements: FlyBehavior.java, FlyWithWings.java et
FlyNoWay.java,
 Le comportement : QuackBehavior.java, Quack.java, Squeak.java et
MuteQuack.java
 Les classes MallardDuck.java et WoodenDuck.java
 Tester toutes les méthodes des canards créés dans un main:
MallardDuckSim.java

31
Riadh BEN HALIMA [Design patterns]
SimUDuck :
Le comportement dynamique (19/23)
 Changement dynamique de comportement
 Ajouter les méthodes: setFlyBehavior() et setQuackBehavior()
 Développer le canard RedHeadDuck (RedHeadDuck.java)
 Implanter le nouveau comportement "vole-force-fusée"
FlyRocketPowered (FlyRocketPowered.java)
 Tester le nouveau canard dans un main RedHeadSim.java
 Changer le comportement "voler" de FlyWithWings à
FlyRocketPowered. Penser à utiliser le setter afin d’obtenir ces
deux affichages: "Je peux voler" & "Je vole comme une fusée"

 Donner (et ne pas implémenter) les modifications à faire afin


d’ajouter le comportement manger: eat()
32
Riadh BEN HALIMA [Design patterns]
SimUDuck :
La conception finale (20/23) <<interface>>
QuackBehavior
quack()
Duck
QuackBehavior qbehavior;
FlyBehavior fbehavior; implements
Quack Squeak MuteQuack
performQuack()
swim() quack(){ quack(){ quack(){
display() //quack //squeak //rien-ne
performFly() } //rubber //cancane pas
setFlyBehavior() } }
setQuackBehavior() <<interface>>
\\autre méthodes FlyBehavior
fly()
extends
MallardDuck WoodenDuck
implements
display(){ display(){
\\canard Mallard \\canard en bois FlyWithWings FlyNoWay
} } fly() { fly() {
//vole //ne vole pas
Algorithmes } }
33
interchangeables Riadh BEN HALIMA [Design patterns]
SimUDuck :
Composition/Héritage (21/23)
 Has-a: liaison intéressante
 Chaque canard possède un FlyBehavior et QuackBehavior qui
délègue flying et quacking
 Composition (mettre les 2 classes ensemble)
 Encapsuler une famille d’algorithmes dans leur propre ensemble
de classes
 Remplacer l’héritage pour favoriser le changement dynamique du
comportement

 Règle 3: Favoriser la composition sur l’héritage

34
Riadh BEN HALIMA [Design patterns]
SimUDuck :
Notre premier patron (22/23)

 Notre premier patron: STRATEGIE


Le patron stratégie cherche principalement à séparer
un objet de ses comportements/algorithmes en
encapsulant ces derniers dans des classes à part.

Pour ce faire, on doit alors définir une famille de


comportements ou d’algorithmes encapsulés et
interchangeables.

35
Riadh BEN HALIMA [Design patterns]
Diagramme de classes du patron (23/23)

strategy <<interface>>
Context
Strategy
ContextInterface() AlgorithmInterface()

ConcreteStrategyA ConcreteStrategyB ConcreteStrategyC


AlgorithmInterface() AlgorithmInterface() AlgorithmInterface()

Première implémentation du
comportement Deuxième implémentation du
comportement

36
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
 Patron de l’OO (stratégie)
 Le patron stratégie définit une famille d’algorithmes, les
encapsule, et les rend interchangeable. Ce patron laisse
l’algorithme varier indépendamment du client qu’il l’utilise.

37
Riadh BEN HALIMA [Design patterns]
Récapitulatif (2/2)
 Connaitre les bases de l’OO ne fait pas de toi un bon concepteur
 Les meilleurs conception OO sont réutilisable, flexible et
maintenable
 Les patrons nous guident à construire des systèmes avec les bonnes
qualités de la conception OO
 Les patrons sont des expériences prouvées dans l’OO
 Les patrons ne donnent pas de code, mais plutôt des solutions
générales pour des problèmes de conception
 Les patrons de sont pas inventés, mais découverts
 La majorité des patrons et des principes adresses les problèmes de
changement dans le logiciel
 On essaie toujours de prendre ce qui varie des systèmes et on
l’encapsule
 Les patrons offrent un langage partagé qui peut maximiser la valeur
de la communication avec les autres développeurs

38
Riadh BEN HALIMA [Design patterns]
Exercice (1/2)
 Ci-dessous, on donne l’ensemble de classes et interfaces d’un jeu
d’action et d’aventure. Il y a des classes d’individus avec des
classes pour les comportements d’armes que les individus
peuvent utiliser. Chaque individu peut utiliser une seule arme à la
fois, mais peut la changer à tout moment durant le jeu. La tâche
demandée est d’ordonner le tout.
Individu ComportementCouteau Chevalier
ComportementArme arme; utiliseArme(){\\abattre avec display(){….}
combattre() couteau }
display ()
Archer ComportementArcFleche
Reine
display(){….} utiliseArme(){\\abattre avec arc
display(){….} et flèche}
ComportementArme
setArme(ComportementArme ca) {
utiliseArme();
this.arme=ca; ComportementEpee
}
utiliseArme(){\\abattre avec
39 épée}
Riadh BEN HALIMA [Design patterns]
Exercice (2/2)
1. Arranger les classes
2. Identifier les classes, les classes abstraites des interfaces
3. Relier les entités pas des flèches ou:
1. représente extends
2. représente implements
3. représente has-a
4. Mettre la méthode setArme() dans la classe
correspondante
5. Implémenter et tester cette conception dans un main.
Penser à changer dynamiquement le comportement de
l’archer après avoir finir ces arcs.

40
Riadh BEN HALIMA [Design patterns]
Le patron
"Observer"

41
Riadh BEN HALIMA [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
météorologique et les prévisions météo.
 Poursuivre les conditions météorologiques (Température,
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.

42
Riadh BEN HALIMA [Design patterns]
Station météo:
Analyse (2/14)

Afficher
Capteur
d’humidité Tirer (pull)

Capteur de
température
Weather Station
WeatherData
Object
Capteur de Conditions
pression courantes

Le fournisseur ENIS-METEO A implémenter

 Il faut créer une application qui utilise l’objet WeatherData afin de


mettre à jour trois afficheurs: les conditions courantes, les statistiques
météorologiques & les prévisions météo.
43
Riadh BEN HALIMA [Design patterns]
Station météo:
Conception (3/14)
WeatherData Ces trois méthodes retournent les
mesures les plus récentes
getTemperature()
getHumidity()
getPressure() Cette méthode sera appelée chaque
fois qu’on met à jour les mesures
measurementChanged()
\\autre méthodes

 Nous allons développer trois afficheurs:


 conditions courantes (CurrentConditionsDisplay.java)
 statistiques météorologique (StatisticsDisplay.java)
 prévisions météo (ForecastDisplay.java)
 Rq: On ne s’intéresse pas à la manière dont les variables sont fixées.
On suppose que l’objet WeatherData connait comment les mettre à
jour à partie de la station ENIS-METEO
44
Riadh BEN HALIMA [Design patterns]
Station météo:
Une solution possible (4/14)
public class WeatherData
Récupérer les mesures les
{ plus récentes
//attributs
void measurementChanged(){
float temperature = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();

currentConditionsDisplay. update(temperature, humidity, pressure);


statisticsDisplay. update(temperature, humidity, pressure);
forecastDisplay. update(temperature, humidity, pressure);
}
//autres méthodes
}
Mettre à jour les afficheurs
avec les nouvelles mesures

45
Riadh BEN HALIMA [Design patterns]
Station météo:
Constat (5/14)
 Problème : En codant des implémentations concrètes, on ne peut pas
ajouter/supprimer d’autres afficheurs, sans faire un changement du
programme!
 Rq: on peut au moins utiliser une interface qui contient la
méthode update()
 Solution : le patron observer
 Exemple:
1. Une maison d’édition commence l’édition d’un journal
2. Vous vous inscrivez à ce journal, et pour chaque nouvelle
édition, vous recevez votre copie
3. Vous vous désinscrivez lorsque vous ne voulez plus recevoir
des journaux
4. Les gens, les hôtels etc. peuvent constamment s’inscrire et se
désinscrire à ce journal
46
Riadh BEN HALIMA [Design patterns]
Station météo:
Publier/Souscrire= Patron Observer (6/14)
Les observers sont
inscrits au Sujet afin de
Lorsque les données du sujet changent, recevoir les mise à jour
les observateurs sont notifiés
L’objet Sujet gère des
octets de données
2

Les objets Observers

47
Riadh BEN HALIMA [Design patterns]
Station météo:
Patron Observer : S’inscrire (7/14)

Les objets Observers

48
Riadh BEN HALIMA [Design patterns]
Station météo:
Patron Observer : Notification(8/14)
Maintenant, le Mouse est un
observer, il reçoit les
notifications du Sujet

Nouvel entier

Les objets Observers

49
Riadh BEN HALIMA [Design patterns]
Station météo:
Patron Observer : Désinscrire (9/14)

Les objets Observers

50
Riadh BEN HALIMA [Design patterns]
Station météo:
Patron Observer : Notification (10/14)
Maintenant, le Dog n’est plus un
observer, il ne reçoit plus les
notifications du Sujet

12

12

Nouvel entier

Les objets Observers

51
Riadh BEN HALIMA [Design patterns]
Station météo:
Le patron Observer (11/14)
 Définition: Observer
 Le patron observer définit une dépendance 1-à-plusieurs
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.

1-à-plusieurs

Les objets dépendants


8

8
8

L’objet qui tient les mises


Mise à jour/notification
à jours Les objets Observers
automatique
52
Riadh BEN HALIMA [Design patterns]
Station météo:
Diagramme de classes du patron (12/14)
Chaque Subject possède Tous les observers potentiels doivent
un tableau d’observers implémenter l’interface "Observer"
Les objets utilisent
cette interfaces pour <<interface>> <<interface>>
s’inscrire comme Subject Observer
observer et aussi se
désinscrire registerObserver(..) * update(..)
removeObserver(..)
notifyObservers()

ConcreteSubject ConcreteObserver
Le ConcreteSubject registerObserver(..){…} update(..)
hérite toujours de removeObserver(..) {…} //autres méthodes
l’interface Subject. Il
notifyObservers() {…}
implémente la méthode
notifyObservers() qui
est sensée mettre à getState()
Toutes classes qui implémente l’observer est
jour l’observer quand setState() un observer concret. Chaque observer
l’état change. concret s’inscrit auprès d’un sujet concret
pour recevoir l’update
53
Riadh BEN HALIMA [Design patterns]
Station météo:
La puissance du couplage faible (13/14)
 Deux objets, qui sont faiblement couplés, entrent en interaction
avec très peu de connaissance de l’un envers l’autre.
 Le patron Observer permet de réaliser une conception ou le
Subject et l’Observer sont faiblement couplés
 Couplage faible : plus d’indépendance et de flexibilité
 La seule chose que le Subject a besoin de connaitre sur l’Observer est
qu’il implémente une certaine interface.
 On peut ajouter/remplacer/supprimer un observer à tout moment
sans toucher au Subject
 On peut réutiliser le Subject ou l’Observer facilement parce qu’ils ne
sont pas fortement couplés.
 La modification du Subject ou de l’Observer n’affecte pas l’autre classe
 Règle 4: Opter pour une conception faiblement couplée
entre les objets qui interagissent
54
Riadh BEN HALIMA [Design patterns]
Station météo:
La conception finale (14/14)
observers
<<interface>> <<interface>>
Subject Observer
registerObserver(..) * update(…)
removeObserver(..) display()
notifyObservers()

CurrentConditionsDisplay
WeatherData update(…)
display()
registerObserver(..){…}
removeObserver(..) {…}
notifyObservers() {…}
StatisticsDisplay ForecastDisplay
getTemperature()
getHumidity() update(…) update(…)
getPressure() display() display()
measurementChanged()

Les trois afficheurs pointent sur le WeatherData afin


subject
de permettre leur inscription et leur désinscription
55
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 entre les objets
qui interagissent
 Patron de l’OO
 Strategy: définit une famille d’algorithmes interchangeables
 Observer: définit une dépendance1-à-plusieurs entre objets, de
façon que pour chaque changement de l’état d’un objet , ses
dépendants sont notifiés et mis à jour automatiquement.

56
Riadh BEN HALIMA [Design patterns]
Récapitulatif (2/2)
 Le patron observer définit une relation 1-à-plusieurs entre
objets.
 Le Subject/Observable met à jour les observers à travers
une interface commune.
 L’observer est faiblement couplé avec le Subject. Ce
dernier ne connait rien d’eux à part qu’ils implémentent
l’interface Observer.
 On peut récupérer les données du Subject en mode
pull/push. (Le Subject fait le push, semble plus correct)
 On ne dépend pas d’un ordre spécifique de notification
entre les observers
 Java possède plusieurs implémentations de ce patron
57
Riadh BEN HALIMA [Design patterns]
Exercice 1
1. Appliquer le patron Observer sur l’application station météo en se
basant sur les implémentations du Subject et de l’Observer offertes
par Java :
 java.util.Observable : Subject du patron (Attention! Il s’agit d’une classe)
 java.util.Observer : Observer du patron

<<interface>>
Pour dire Observable Observer
explicitement que
l’état de l’observable addObserver(Observer o) update(Observable o, Object
a changé. Elle ne fait deleteObserver(Observer o) arg)
pas la notification. notifyObservers(Object arg)
setChanged() Ou
o est utilisé pour récupérer le message, et
arg est un argument à passer à l’observer
Il s’agit d’une classe. Les méthodes (mettez le à null, si pas besoin de passer un argument)
offertes sont déjà implémentées.

2. Expliquer comment Java utilise le patron observer pour la gestion


des évènements sur les interfaces graphiques.
58
Riadh BEN HALIMA [Design patterns]
Exercice2 (1/2)
 Les méthodes getSecondes, getMinutes et getHeures permettent d’accéder aux
valeurs des secondes, minutes et heures respectivement. La classe Chrono est une
classe concrète implantant l’interface de la classe AbstractChrono. Lorsqu’une
seconde passe (tick()), les Displays doivent afficher les heures, les minutes et les
secondes et les Sonneries doit faire entendre un tintement chaque heure.

Sonnerie1 AbstractChrono DisplayNumerique


int seconde;
int minute;
ding() int heure; afficheTempsEcoule()
getSeconde()
getMinute()
getHeure()
tick()

Sonnerie2 Chrono DisplayAiguille

ding()
afficheTempsEcoule()

59
Riadh BEN HALIMA [Design patterns]
Exercice2 (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 tout en considérant le
programme principal suivant:

 public static void main(String[] args) {


AbstractChrono chrono = new Chrono();
SonnerieObserver sonnerie1= new Sonnerie1(chrono );
SonnerieObserver sonnerie2 = new Sonnerie2(chrono );
DisplayObserver display1= new DisplayNumerique(chrono );
DisplayObserver display2 = new DisplayAiguille(chrono );
for(int i=3595;i<5000;i++)
{
try{Thread.sleep(1000);} catch(InterruptedException e) {System.out.print("erreur");}
chrono.tick((i%3600)%60, (int)((i%3600)/60), (int)(i/3600));
}}
60
Riadh BEN HALIMA [Design patterns]
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 :
 Beverage.java, Coffee.java, CoffeeWithMilk.java,
CoffeeWithMilkWithWhip.java, Tea.java, TeaWithMint.java,
TeaWithMintWithPine.java, 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()

0.300 0.200 0.500

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

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() methodA() Les décorateurs implémentent


methodB() methodB() la même interface (ou classe
//autres méthodes //autres méthodes abstraite) que l’objet qu’ils
décoreront

ConcreteDecoratorA ConcreteDecoratorB
Le ConcreteDecorator possède
un attribut qui représente l’objet Component WrappedObj Component WrappedObj
qu’il décore. Il peut ajouter ses Object newState
propres méthodes et attributs. methodA()
methodB() methodA()
Newbehavior() methodB()
70
//autres méthodes //autres méthodes
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 java.io doit donner plus de
sens, puisqu’il se base largement sur ce patron.

FileInputStream est un composant


décoré. Le package java.io 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 java.io
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
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 avec AirBag
System.out.println("Son prix est:"+ v2.cost());
//affiche: Son prix est 22200

78
Riadh BEN HALIMA [Design patterns]
Le patron
"Factory"

79
Riadh BEN HALIMA [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
Pizza pizza=new Pizza(); interface, sauf qu’on ne peut pas
instancier l’un des deux derniers!
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}

 On veut plus qu’un type de pizza….

80
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Plusieurs types de pizza (2/37)
 Pizzeria: Créer plusieurs types de pizza
On passe le type du pizza à travers
Pizza orderPizza( String type ){
le méthode orderPizza()
Pizza pizza;

if (type.equals("cheese"){
pizza=new CheesePizza();
} else if (type.equals("greek"){ Selon le type de pizza, on crée la
pizza=new GreekPizza(); pizza concrète, et on la place dans
} else if (type.equals("pepperoni"){ la variable "pizza" (interface et
pizza=new PepperoniPizza(); classe mère des pizzas).
}

pizza.prepare();
pizza.bake(); Une fois on a la pizza, on prépare la sauce, le
pizza.cut(); nappage (tomate/crême fraiche) et le fromage ,
pizza.box(); puis on la fait cuire, la coupe, et on la met dans
return pizza; une boite.
}

81
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Autres types de pizza (3/37)
 Les concurrents ont ajouté un nouveau type de pizza: (avec Calamars)
 Ajouter ClamPizza au menu
 On n’a pas vendu beaucoup de GreekPizza dernièrement
 Suspendre GreekPizza du menu
Pizza orderPizza(String type){
Pizza pizza;
if (type.equals("cheese"){
Ce code est NON fermé

pizza=new CheesePizza();
pour la modification!

} else if (type.equals("greek"){ Partie variable: On modifie


pizza=new GreekPizza(); le code autant que la
} else if (type.equals("pepperoni"){ sélection de pizza change.
pizza=new PepperoniPizza();
} else if (type.equals("clam"){
pizza=new ClamPizza();
}
pizza.prepare();
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;
82 }
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Encapsuler la création (4/37)
 Règle 1 de l’OO: Encapsuler ce qui varie
if (type.equals("cheese"){
pizza=new CheesePizza();
} else if (type.equals("pepperoni"){
pizza=new PepperoniPizza();
} else if (type.equals("clam"){
pizza=new ClamPizza();
}
Pizza orderPizza(String type){
Pizza pizza;

On place ce code dans un objet qui s’occupera de la


création des pizzas concrètes. Si un autre objet a besoin
d’une pizza concrète, c’est cet objet qu’il faut appeler.
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}

83 On attribue le nom
RiadhFactory à ce nouvel
BEN HALIMA objet
[Design patterns]
PizzaStore :
Un Simple Factory (5/37)
 Le seul rôle de SimplePizzaFactory est de créer des pizzas pour ces
clients
Initialement, on définit la méthode
createPizza() dans le Factory. C’est la
public class SimplePizzaFactory { méthode que tous les clients utilisent
public Pizza createPizza(String type){ pour instancier des nouveaux objets.
Pizza pizza=null;

if (type.equals("cheese"){
pizza=new CheesePizza();
} else if (type.equals("pepperoni"){
pizza=new PepperoniPizza();
} else if (type.equals("clam"){
pizza=new ClamPizza();
} C’est le code qu’on a retirer de la
méthode orderPizza().
return pizza;
}
}

84
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Retravaillons la classe PizzaStore (6/37)
 Maintenant, PizzaStore utilise SimplePizzaFactory pour créer des pizzas
PizzaStore récupère la
public class PizzaStore {
référence du factory à
private SimplePizzaFactory factory;
travers le constructeur.
public PizzaStore(SimplePizzaFactory factory){
this.factory=factory;
}
public Pizza orderPizza(String type){
Pizza pizza;
pizza=factory.createPizza(type);
pizza.prepare();
pizza.bake(); La méthode orderPizza() utilise le
pizza.cut(); factory pour créer ses pizzas.
pizza.box();
return pizza;
}
}
 Noter qu’on a remplacé l’opérateur new par une méthode concrète
 Plus d’instanciation concrète ici
85
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Le diagramme de classes (7/37)
Elle doit être la seule partie de On définit Pizza comme
notre application qui pointe vers les classe abstraite, avec quelque
classes des pizzas concrètes implémentations qui peuvent
être redéfinies
PizzaStore SimplePizzaFactory Pizza
prepare()
orderPizza() createPizza() bake()
cut()
box()
La méthode de création
est souvent statique

CheesePizza ClamPizza
PizzaStore crée des PepperoniPizza
instances à travers
SimplePizzaFactory

Chaque produit implémente la classe abstraite Pizza


 Actuellement, Simple Factory n’est pas un patron. C’est plutôt un
"style" de programmation
86
Riadh BEN HALIMA [Design patterns]
Résumé
 1 seule méthode create  style de programmation

 Plusieurs méthodes create avec relation d’héritage: DP

* create() je la place dans des create () abstract


classes métiers existantes: DP
Factory Method

* Je crée des nouvelles classes


create () create ()
dédiées pour placer les
méthodes create(): DP
Abstract Factory retourne une instance
retourne une instance
d’une première famille d’une 2ème famille d'objets
d'objets

87
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Franchise de PizzaStore (8/37)
 Objectif: Franchiser PizzaStore: Créer plusieurs stores dans
plusieurs villes (Sfax, Tunis, etc.)
 Besoin: Développer une application qui gère la création des
pizzas pour chaque store
 Chaque store offre différent types de pizza
 Conception: OO
Moins de fromage dans
les pizzas, etc.

Des pizzas familles de


grande taille, beaucoup
de fromage, etc.
88
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Différent styles des PizzaStores (9/37)
 Si on prépare les mêmes pizzas Créer des pizzas à travers
SfaxPizzaFactory
SfaxPizzaFactory sffactory = new SfaxPizzaFactory();
PizzaStore sfstore = new PizzaStore(sffactory);
sfstore.orderPizza("cheese"); Créer les mêmes pizzas à
travers TunisPizzaFactory

TunisPizzaFactory tnfactory = new TunisPizzaFactory();


PizzaStore tnstore = new PizzaStore(tnfactory);
tnstore.orderPizza("cheese");

 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
touches d’amélioration des procédures de
mon PizzaStore

89
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Le framework de PizzaStore (10/37)
 On donne la liberté aux franchises de créer leurs propres styles
 Déplacer la création dans une la méthode createPizza() et garder la
même méthode orderPizza() pour tous les stores
 Chaque région l’étend afin de spécifier son propre style
public abstract class PizzaStore {

public Pizza orderPizza(String type){


Pizza pizza; CreatePizza() est une
méthode PizzaStore plutôt
pizza = createPizza(type); que dans le Factory
pizza.prepare();
pizza.bake(); Tout ceci apparaît le même
pizza.cut(); On a transféré notre objet
pizza.box(); Factory dans cette méthode
return pizza;
}
public abstract Pizza createPizza(String type);
}
Notre "méthode Factory" est
90
maintenant abstraite dans PizzaStoreRiadh BEN HALIMA [Design patterns]
PizzaStore :
Les styles de pizzas (11/37)
PizzaStore Méthode abstraite, à définir
dans les classes dérivées
createPizza()
orderPizza()

Créer des pizzas


Créer des pizzas SfaxStylePizzaStore TunisStylePizzaStore avec les ingrédients
avec les ingrédients spécifiques à Tunis
spécifiques à Sfax createPizza() createPizza()

Pizza createPizza(String type) { Pizza createPizza(String type) {


Pizza pizza=null; Pizza pizza=null;
if ("cheese".equals(type)){ if ("cheese".equals(type)){
pizza=new SfaxStyleCheesePizza(); pizza=new TunisStyleCheesePizza();
} else if ("pepperoni".equals(type)){ } else if ("pepperoni".equals(type)){
pizza=new SfaxStylePepperoniPizza(); pizza=new TunisStylePepperoniPizza();
} else if ("clam".equals(type)){ } else if ("clam".equals(type)){
pizza=new SfaxStyleClamPizza(); pizza=new TunisStyleClamPizza();
} }
return pizza; return pizza;
} }

91
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Un PizzaStore de Style Sfaxien (12/37)
 Les bénéfices d’une franchise
 On obtient des fonctionnalités des pizzas communes (prepare(), bake(), cut() et box())
 Chaque région définit sa propre méthode createPizza() qui spécifie son style de pizza
public class SfaxStylePizzaStore extends PizzaStore {
Pizza createPizza(String type) {
Pizza pizza=null; On hérite la méthode
if ("cheese".equals(type)){ orderPizza() de PizzaStore
pizza=new SfaxStyleCheesePizza();
} else if ("pepperoni".equals(type)){ On implémente createPizza()
pizza=new SfaxStylePepperoniPizza(); puisqu’elle est abstraite:
} else if ("clam".equals(type)){ C’est la "méthode Factory"
pizza=new SfaxStyleClamPizza();
}
return pizza; Créer des pizzas du style sfaxien!!
}
 Rq: Toutes les responsabilités d’instanciation sont déplacées vers la méthode
createPizza() qui agit comme un factory
 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-
classes
92
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Commander des pizzas (13/37)
Je voudrai une pizza Je voudrai une pizza
de grande taille avec beaucoup de taille moyenne avec
de fromage du style peu de fromage et au thon
tunisien du style sfaxien

PizzaStore tunisps = PizzaStore sfaxps =


new TunisStylePizzaStore(); new SfaxStylePizzaStore();

Instance du store spécifique

Prendre des commandes

tunisps.orderPizza("cheese"); sfaxps.orderPizza("cheese");

C’est une méthode de l’instance tunisps (respectivement


sfaxps), définie dans la classe PizzaStore
93
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Commander des pizzas (14/37)
tunisps.orderPizza("cheese"); sfaxps.orderPizza("cheese");

La méthode orderPizza() appelle createPizza()

Pizza pizza= createPizza("cheese"); Pizza pizza= createPizza("cheese");

La méthode createPizza() est implémentée dans la classe dérivée


Elle retourne une pizza au Elle retourne une pizza au
fromage style tunisien fromage style sfaxien

On termine la préparation
pizza.prepare(); pizza.prepare();
pizza.bake(); pizza.bake();
pizza.cut(); pizza.cut();
pizza.box(); pizza.box();

94
De style tunisien DeRiadh
styleBEN
sfaxien
HALIMA [Design patterns]
PizzaStore :
Implémenter (15/37) Les créateurs
PizzaStore
 Implémenter les classes: createPizza()
PizzaStore.java, SfaxPizzaStore.java orderPizza()
Factory Method
et TunisPizzaStore.java
SfaxStylePizzaStore TunisStylePizzaStore
createPizza() createPizza()

 Implémenter les classes: Pizza.java, Les produits


SfaxStyleCheesePizza.java, Pizza
SfaxStyleClamPizza.java,
SfaxStylePepperoniPizza.java,
TunisStyleCheesePizza.java,
TunisStyleClamPizza.java, SfaxStyleCheesePizza TunisStyleCheesePizza
TunisStylePepperoniPizza.java SfaxStyleClamPizza TunisStyleClamPizza

SfaxStylePepperoniPizza
TunisStylePepperoniPizza

95
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Le patron Factory Method (16/37)

 Définition: Factory Method


 Le patron factory method définit une interface de
création des objets, et laisse les classes-dérivées décider de
la classe de l’instanciation. La méthode factory permet à
une classe de déléguer l’instanciation à ces classes dérivées.

96
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Le diagramme de classes du patron (17/37)
La classe mère de tous les produits Le Creator est une classe qui contient
concrets. Elle représente aussi le type l’implémentation de toutes les
générique qui référence vers les méthodes qui manipulent le produits,
instances des classes concrètes à l’exception de factoryMethod()

Product Creator La méthode abstraite


factoryMethod() factoryMethod() que tous les
anOperation() dérivés de la classe Creator
implémentent

Le ConcretCreator
implémente la méthode
factoryMethod(), qui produit
ConcretProduct ConctretCreator 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
97
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Un PizzaStore dépendant (18/37)
 Hypothèse: On n’a jamais entendu parler du factory
 Compter le nombre d’objets de pizzas concrètes dont cette classe dépend
 Refaire le compte si on ajoute des pizzas de style bizertin
public class DependentPizzaStore {
Pizza createPizza(String style, String type) {
Pizza pizza=null;
if (style.equals("Sfax")){ Gérer toutes les pizzas de
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();
pizza.cut();
pizza.box();
return pizza;
98 }
} Riadh BEN HALIMA [Design patterns]
PizzaStore :
La dépendance entre les objets (19/37)
 Cette version de PizzaStore dépend de tous les objets pizzas parce qu’elle les crée
directement
 On dit que PizzaStore dépend des implémentations des pizzas parce que chaque
changement des implémentations concrètes des pizzas, affecte le PizzaStore
Si les implémentations des pizzas
changent, on doit modifier PizzaStore

Pour chaque nouveau type de pizza,


on ajoute une autre dépendance dans
la méthode create()
99
Riadh BEN HALIMA [Design patterns]
PizzaStore :
L’inversion de dépendance (20/37)
 Réduire les dépendances aux classes concrètes dans notre code,
est une "bonne chose"
 Le principe qui formalise cette notion s’appelle "principe
d’inversion de dépendance" :
 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.
 Un composant de haut-niveau (PizzaStore) est une classe dont le
comportement dépend des autres composants de bas-
niveau(Pizza)

100
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Appliquons ce principe (21/37)
PizzaStore dépend seulement de
Pizza, qui est une classe abstraite
Pizza est une classe
abstraite.. abstraction
Les classes de pizzas concrètes dépendent
aussi de l’abstraction Pizza, parce qu’elles
implémentent l’interface Pizza

Le "Factory Method"
est la technique la plus
puissante d’adhérence
au principe d’inversion
de dépendance, mais
pas la seule...

101
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Les ingrédients des pizzas (22/37)
 Problème : quelques franchises n’ont pas utilisé la
même procédure de préparation, et ce en substituant
des ingrédients par d’autres de basse qualité, afin
d’augmenter leur marge.
! Il faut assurer la consistance des ingrédients

 Solution : créer un factory qui produit les ingrédients,


et les transporter aux franchises
 Le seul problème avec ce plan : Ce qui est sauce rouge
à Sfax, n’est pas sauce rouge à Tunis
 Il y a un ensemble d’ingrédients à transporter à Sfax, et un
autre ensemble à transporter à Tunis
102
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Les menus des pizzas (23/37)

Sfax Tunis
Pizza Menu Pizza Menu
Nous avons les mêmes
Cheese Pizza Cheese Pizza
Sauce marinara, Parmesan,
familles de produits, mais
différente implémentations Sauce tomate prune, Mozzarella,
Emmental Roquefort
selon la région
Clam Pizza Clam Pizza
Sauce marinara, Parmesan, Sauce tomate prune, Mozzarella,
Clovis, Olive verte Palourde, Olive noire
Pepperoni Pizza Pepperoni Pizza
Sauce marinara, Parmesan, Sauce tomate prune, Mozzarella,
Aubergine, Poivron, Olive verte Épinard, Poivre, Olive noire

103
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Les familles des ingrédients (24/37)
Chaque famille correspond à un Tunis
type de sauce, un type de
fromage, un type de légume, et un PlumTomatoSauce
Mozzarella
type d’olive (et d’autres types)

Spinach
BlackOlive

Sfax
MarinaraSauce
Parmesan

Toutes les PizzaStores utilisent


Eggplant les mêmes composants, mais
GreenOlive
chaque région possède une
implémentation spécifique de ces
composants

104
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Les factories des ingrédients (25/37)
 Le factory est le responsable de la création de la pâte, la sauce,
le fromage, etc.
Pour chaque ingrédient, on
public interface PizzaIngredientFactory {
crée une create() méthode
Dough createDough();
dans notre interface
Sauce createSauce();
Cheese createCheese();
Veggies[] createVeggies();
Clam createClam();
} Beaucoup de nouvelles
classes, une par ingredient
 A faire :
 Construire un facotry pour chaque région: une sous-classe de
PizzaIngredientFactory qui implémente chaque create() méthode
 Implémenter un ensemble de classes d’ingrédients, à utiliser par les
factories tels que: OliveVerte, Mozzarella, SauseMarinara, etc.
 Lier ceci avec notre ancien code de PizzaStore, tout en travaillant
nos factories d’ingrédients
105
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Les factories de Sfax (26/37)

public class SfaxPizzaIngredientFactory implements PizzaIngredientFactory


{
public Dough createDough(){
return new ThinDough(); Pour chaque famille d’ingrédient, on
} crée la version sfaxienne
public Sauce createSauce(){
return new MarinaraSauce();
}
public Cheese createCheese(){
return new Parmesan();
}
public Veggies[] createVeggies(){
Veggies veggies[]={new Garlic(), new Onion(), new EggPlant()};
return veggies;
}
public Clam createClam(){
return new Clovis();
} Palourde() pour le
} cas de tunis
106
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Les factories de Tunis (26’/37)

public class TunisPizzaIngredientFactory implements PizzaIngredientFactory


{
public Dough createDough(){
return new CrunchyDough(); Pour chaque famille d’ingrédient, on
} crée la version Tunisoise
public Sauce createSauce(){
return new PlumTomatoSauce();
}
public Cheese createCheese(){
return new Mozzarella();
}
public Veggies[] createVeggies(){
Veggies veggies[]={new Garlic(), new Onion(), new Spinach()};
return veggies;
}
public Clam createClam(){
return new Palourde ();
}
}
107
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Retravaillons la classe Pizza (27/37)

public abstract class Pizza { Les ingrédients d’une paizza


protected String name; (liste non-exhaustive)
protected Dough dough;
protected Sauce sauce;
La collecte des ingrédients se fait dans cette
protected Cheese cheese;
méthode (à travers un factory d’ingrédients)
protected Veggies veggies[];
qui sera définie par les classes dérivées
protected Clam clam;
public abstract void prepare();
public void bake(){
System.out.println("Cuire durant 25mn à 280°");}
public void cut(){
System.out.println("Couper en morceaux à la diagonale");}
public void box(){
System.out.println("Placer la pizza dans un boitier officiel");}
public void setName(String s){
name=s;}
public String getName(){ Les autres méthodes sont les
return name;} mêmes (à l’exception de prepare())
}
108
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Retravaillons les classes des Pizzas (28/37)
Pour faire la pizza, on besoin d’un
factory. Chaque classe Pizza prend le
factory à travers son constructeur

public class CheesePizza extends Pizza {


private PizzaIngredientFactory ingredientfactory;
public CheesePizza(PizzaIngredientFactory ingredientfactory){
this.ingredientfactory = ingredientfactory;
}
@Override
public void prepare(){
System.out.println("Préparons " + name);
dough = ingredientfactory.createDough();
sauce = ingredientfactory.createSauce();
cheese = ingredientfactory.createCheese();
}
Chaque fois que la méthode prepare()
} a besoin d’ingrédient, elle appelle le
factory pour le produire

109
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Retravaillons les classes des Pizzas (29/37)

Un factory pour chaque type de Pizza

public class ClamPizza extends Pizza {


private PizzaIngredientFactory ingredientfactory;
public ClamPizza(PizzaIngredientFactory ingredientfactory){
this.ingredientfactory = ingredientfactory;
}
@Override
public void prepare(){
System.out.println("Préparons " + name);
dough = ingredientfactory.createDough();
sauce = ingredientfactory.createSauce();
cheese = ingredientfactory.createCheese();
clam= ingredientfactory.createClam(); Pour faire une ClamPizza, la méthode
} prépare les ingrédients
correspondants de son factory local.
} Si c’est le factory de sfax, on
va préparer des clovis

110
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Retravaillons les PizzaStores (30/37)
Le store de Sfax est composé d’un
factory sfaxien d’ingrédients/
public class SfaxPizzaStore extends PizzaStore {
private PizzaIngredientFactory sfaxIngredientfactory =
new SfaxPizzaIngredientFactory();
@Override
public Pizza createPizza(String item) {
Pizza pizza = null; On passe à chaque pizza le factory
if (item.equals("cheese")){ censé créer ses ingrédients
pizza = new CheesePizza(sfaxIngredientfactory);
pizza.setName("Sfax Style Cheese Pizza");
} else if (item.equals("pepperoni")){
pizza = new PepperoniPizza(sfaxIngredientfactory);
pizza.setName("Sfax Style Pepperoni Pizza");
} else if (item.equals("clam")){
pizza = new ClamPizza(sfaxIngredientfactory);
pizza.setName("Sfax Style Clam Pizza");
}
return pizza;
}
}
111
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Factories (31/37)
Définit l’interface

Offre les
Abstract Ingredient Factory
implémentations
des ingrédients

Sfax Tunis

De l’abstract factory, on dérive


plusieurs concrets factories qui
produisent les mêmes produits, mais avec
différentes implémentations
En passant (au constructeur) une variété
de factories, on obtient une variété
d’implémentations, tout en gardant le
même code du client
112
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Commander des pizzas (32/37)
Je voudrai une pizza Je voudrai une pizza
de grande taille avec beaucoup de taille moyenne avec
de fromage du style peu de fromage et au thon
tunisien du style sfaxien

PizzaStore tunisps = PizzaStore sfaxps =


new TunisPizzaStore(); new SfaxPizzaStore();

Instance du store spécifique

Prendre des commandes

tunisps.orderPizza("cheese"); sfaxps.orderPizza("cheese");

C’est une méthode de l’instance tunisps (respectivement


sfaxps), définie dans la classe PizzaStore
113
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Commander des pizzas (33/37)
tunisps.orderPizza("cheese"); sfaxps.orderPizza("cheese");

La méthode orderPizza() appelle initialement createPizza()

Pizza pizza= createPizza("cheese"); Pizza pizza= createPizza("cheese");

La méthode createPizza() implique le factory d’ingrédients


Pizza pizza= new Pizza pizza= new
CheesePizza(tunisIngeredientFactory); CheesePizza(sfaxIngeredientFactory);

Chaque instance de pizza est associée à un factory d’ingrédients

La méthode prepare() est appelée et chaque factory est appelé pour produire les
ingrédients de la région

void prepare(){ void prepare(){

114 CheesePizza à la tunisoise CheesePizza à la sfaxienne


Riadh BEN HALIMA [Design patterns]
PizzaStore :
Commander des pizzas (34/37)
void prepare(){ void prepare(){
dough = factory.createDough(); dough = factory.createDough();
// Pâte coustillante // Pâte mince
sauce = factory.createSauce(); sauce = factory.createSauce();
// Sauce tomate prune // Sauce marinara
cheese = factory.createCheese(); cheese = factory.createCheese();
// Mozzarella, Roquefort // Parmesan, Emmental
} }
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();

115
De style tunisien DeRiadh
styleBEN
sfaxien
HALIMA [Design patterns]
PizzaStore :
Le patron Abstract Factory (35/37)

 Définition: Abstract Factory


 Le patron abstract factory offre une interface de
création de familles d’objets dépendants (en relation), sans
spécifier leurs classes concrètes.

116
Riadh BEN HALIMA [Design patterns]
PizzaStore :
Le diagramme de classes du patron (36/37)
Le client est composé au moment de
l’exécution par un factory concret
L’abstract factory définit un Client
ensemble de méthodes pour la
production des produits Une famille de produits
<<interface>>
AbstractProductA
<<interface>>
AbstractFactory
createProductA()
createProductB()
ProductA2 ProductA1

<<interface>>
AbstractProductB

ConcretFactory1 ConcretFactory2
createProductA() createProductA()
createProductB() createProductB() ProductB2 ProductB1
Les factories concrets implémentent les
différences familles de produits
117
Riadh BEN HALIMA [Design patterns]
PizzaStore :
La conception finale (37/37) Les clients de l’abstract factory
sont les stores de Sfax et de Tunis
SfaxPizzaStore
L’abstract factory définit un <<interface>>
l’ensemble des produits qu’on a Clam
besoin pour faire une pizza

<<interface>> Clovis Palourde


PizzaIngredientFactory
createSauce() <<interface>>
createCheese() Sauce
createClam()
//etc.

MarinaraSauce PlumTomatoSauce

TunisPizzaIngred SfaxPizzaIngredi <<interface>>


ientFActory entFactory Cheese
createSauce() createSauce()
createCheese() createCheese()
createClam() createClam()
Parmesan Mozzarella
La tâche des factories est de produire les
ingrédients spécifiques à chaque région
118 Chaque factory produit différent implémentation
de chaque famille deRiadh BEN HALIMA [Design patterns]
produits
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
 Dépendre des abstractions. Ne jamais dépendre de classes concrètes
 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.
 Factory Method: définit une interface de création des objets, et laisse les
classes-dérivées décider de la classe de l’instanciation.
 Abstract Factory: offre une interface de création de familles d’objets
dépendants, sans spécifier leurs classes concrètes
119
Riadh BEN HALIMA [Design patterns]
Récapitulatif (2/2)
 Tous les factories encapsule la création des objets
 Malgré qu’il n’est un vrai patron, le "Simpe Factory" est une manière simple
de découplage de clients des concrètes classes
 Factory Method repose sur l’héritage: La création d’objet est déléguée aux
sous-classes qui implémentent la méthode factory de création d’objets
 Abstract Factory repose sur la composition d’objets: La création d’objet est
implémentée dans une méthode exposée dans l’interface du factory
 Tous les patrons factories soutiennent le faible couplage entre notre
application et les classes concrètes.
 L’intension de Factory Method est de permettre à une classe de reporter
l’instanciation à ses sous-classes.
 L’intension d’Abstract Factory est de créer une famille d’objets en relation
sans dépendre de leurs classes concrètes
 L’inversion de dépendance nous guide afin d’éviter les dépendances des
classes concrètes, et s’efforcer pour les abstractions
 Factories sont des techniques puissantes de codages des abstractions et non
des classes concrètes
120
Riadh BEN HALIMA [Design patterns]
Le patron
"Singleton"

121
Riadh BEN HALIMA [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
 Il y a plusieurs objets dont on a besoin d’une seule instance:
pool d’impression, boite de dialogue, objet qui manipule les
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.

122
Riadh BEN HALIMA [Design patterns]
Singleton :
Créer un singleton (2/12)
 Comment créer un seul objet?
 New MonObjet()
 Et si un autre objet veut créer un MonObjet? Est-ce qu’il peut
appeler new sur MonObjet une autre fois?
 Oui
 Pour toute classe, est ce qu’on peut l’instancier plus qu’une fois?
 Oui (il faut que la classe soit publique)
 Que signifie ce code ?
public class MonObjet{
 C’est une classe qui ne peut pas être instanciée, private MonObjet() {}
car elle possède un constructeur privé }

 Qui peut utiliser ce constructeur?


 Le code de MonObjet est le seul code qui peut l’appeler (dans une méthode)

123
Riadh BEN HALIMA [Design patterns]
Singleton :
Créer un singleton (3/12)
 Comment je peux appeler cette méthode (pour créer une
instance) si je n’ai pas d’instance?
 static public class MonObjet{
 Que signifie ce code. public static MonObjet getInstance() {
}
}

 C’est une méthode statique qui peut être appelée à partir du nom de la
classe : MonObjet.getInstance()
 Si on met les choses ensemble, est ce qu’on peut instancier
MonObjet? public class MonObjet{
private MonObjet(){ }
public static MonObjet getInstance() {
return new MonObjet();
}
}
 Comment faire pour créer une seule instance?
124
Riadh BEN HALIMA [Design patterns]
Singleton :
Implémentation du patron (4/12)
Nous avons une variable statique
pour stocker notre instance

public class Singleton {


private static Singleton uniqueInstance;
private Singleton() {} Le constructeur est déclaré privé.
public static Singleton getInstance(){ Seulement la classe Singleton qui
if (uniqueInstance == null) { peut instancier cette classe
uniqueInstance = new Singleton(); }

return uniqueInstance; Cette méthode nous offre une manière


} pour instancier la classe Singleton

Si uniqueInstance n’est pas à nul, ça veut dire


qu’elle a été créée précédemment
public static void main(String args[]) {
Singleton s= Singleton.getInstance();
}
}

125
Riadh BEN HALIMA [Design patterns]
Singleton :
L’usine de chocolat (5/12)
public class ChocolateBoiler{
private boolean empty; Le code démarre lorsque
private boolean boiled; la casserole est vide
public ChocolateBoiler() {
empty=true; boiled=false;
Pour remplir la casserole, elle
}
public void fill(){ doit être vide. Lorsqu’elle est
if (empty){ pleine, on met empty à false.
//remplir la casserole avec du lait/chocolat
empty=false; boiled=false;
}
} Pour mixer le contenu de la
public void boil(){ casserole, elle doit être pleine et
if (!empty && !boiled){ non déjà mixée. Lorsqu’elle est
//faire bouillir pleine, on met boiled à true.
boiled=true;
}
}
public void drain(){ Pour vide la casserole, elle doit
if (!empty && boiled){ être pleine et déjà mixée. une
//vider la casserole fois vidée, on met empty à true.
empty=true;
}
}
}
126
Riadh BEN HALIMA [Design patterns]
Singleton :
L’usine de chocolat (6/12)
 Améliorer le code de l’usine de chocolat en le transformant en Singleton
public class ChocolateBoiler {
private boolean empty;
private boolean boiled;
private static ChocolateBoiler uniqueInstance;

private ChocolateBoiler() {
empty=true; boiled=false;
}

public static ChocolateBoiler getInstance() {


if (uniqueInstance == null) {
uniqueInstance = new ChocolateBoiler(); }
return uniqueInstance;
}
//reste du code…
}

127
Riadh BEN HALIMA [Design patterns]
Singleton :
Le patron Singleton(7/12)

 Définition: Singleton
 Le patron Singleton assure une seule instance pour une
classe, et offre un point d’accès global à cette classe.

128
Riadh BEN HALIMA [Design patterns]
Singleton:
Le diagramme de classes du patron (8/12)

La variable de classe uniqueInstance


tient la seule instance du Singleton

Singleton
- static uniqueInstance
//autre données utiles

+ static getInstance()
//autre méthodes utiles
La méthode getInstance() est statique.
C’est une méthode de classe qu’on peut
y accéder partout dans le code avec
Singleton.getInstance(). Il s’agit d’une
instanciation facile de cette classe

129
Riadh BEN HALIMA [Design patterns]
Singleton :
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
uniqueInstance
public static ChocolateBoiler
getInstance() null
public static ChocolateBoiler null
getInstance()

if (uniqueInstance == null) null

if (uniqueInstance == null) null


uniqueInstance = new Object1
ChocolateBoiler();
return uniqueInstance; Object1

uniqueInstance = new Object2


ChocolateBoiler();
return uniqueInstance; Object2

130
Riadh BEN HALIMA [Design patterns]
Singleton :
Gestion du multi-threading (10/12)
 Solution 1: synchroniser l’accès à la méthode getInstance()

public class Singleton {


private static Singleton uniqueInstance;
Un seul thread peut accéder,
private Singleton() {} à la fois, à cette méthode

public static synchronized Singleton getInstance(){


if (uniqueInstance == null)
{uniqueInstance = new Singleton();}

return uniqueInstance;
}
//autre méthodes utiles
}

 Inconvénient: synchronized réduit la performance d’un facteur de 100


 Si la méthode getInstance() n’est pas critique pour notre application, on peut
se contenter de cette solution
131
Riadh BEN HALIMA [Design patterns]
Singleton :
Gestion du multi-threading (11/12)
 Solution 2: réduire l’utilisation de la synchronisation dans getInstance()
Le mot clé volatile assure que les threads gèrent la variable
uniqueInstance correctement au moment de son initialisation
public class Singleton {
private volatile static Singleton uniqueInstance;

private Singleton() {}

public static Singleton getInstance(){ On synchronise seulement la


if (uniqueInstance == null) { première fois
synchronized(Singleton.class) {
if (uniqueInstance == null){
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
//autre méthodes utiles
}
*volatile: inclus à java depuis jdk5
132
Riadh BEN HALIMA [Design patterns]
Singleton :
Gestion du multi-threading (12/12)
 Solution 3: 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 {
private static Singleton uniqueInstance = new Singleton();

private Singleton() {}

public static Singleton getInstance(){


return uniqueInstance;
} Il y a déjà une instance, il faut
//autre méthodes utiles juste la retourner
}

 La JVM crée une instance de Singleton lors du chargement de la classe. La JVM


garantit que l’instance va être créée avant que les threads accèdent la variable
statique uniqueInstance.
133
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
 Dépendre des abstractions. Ne jamais dépendre de classes concrètes
 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.
 Abstract Factory: offre une interface de création de familles d’objets
 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

134
Riadh BEN HALIMA [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
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
application multi-thread

135
Riadh BEN HALIMA [Design patterns]
Le patron
"Adapter"

136
Riadh BEN HALIMA [Design patterns]
Le patron adapter :
Les adaptateurs (1/7)

Adaptateur Câble American du PC


Prise Européenne

L’adaptateur convertit une


interface à une autre

137
Riadh BEN HALIMA [Design patterns]
Le patron adapter :
Les adaptateurs dans l’OO (2/7)
Notre Classe du
système vendeur
existant

L’interface ne correspond par à ce qu’on a déjà codé.


Ça ne va pas marché!
(supposant qu’on ne peut pas changer de vendeur)

Notre Adaptateur Classe du


système vendeur
existant

Même code Nouveau code Même code

138
Riadh BEN HALIMA [Design patterns]
Le patron adapter :
Dinde et Canard (3/7)
 Supposant que le dinde marche et cancane comme le canard
Un canard peut cancaner et voler Une simple implémentation du comportement du canard

interface Duck{ class MallardDuck implements Duck{


void quack(); public void quack(){
void fly(); System.out.println("Quack");}
} public void fly(){
System.out.println("Fly");}
}

interface Turkey{ Le dinde ne cancane pas, mais glougloute


void gobble();
void fly(); Le dinde peut voler (courte distance)
}

class WildTurkey implements Turkey{


public void gobble(){
System.out.println("Gobble");}
public void fly(){
System.out.println("Fly for a short distance");}
139 }
Riadh BEN HALIMA [Design patterns]
Le patron adapter :
L’adaptateur du dinde(4/7)
 Supposons qu’on a un manque de canards et on va utiliser des
dindes à leur pace  Il faut écrire un "adapter"
Respecter l’interface des canards

public class TurkeyAdapter implements Duck{


private Turkey turkey;
public TurkeyAdapter(Turkey turkey){
this.turkey=turkey;}
Une référence vers l’objet à adapter
public void quack(){
turkey.gobble();
}
Translation des méthodes
public void fly(){
for(int i=0;i<5;i++)
turkey.fly();
}
}

140
Riadh BEN HALIMA [Design patterns]
Le patron adapter :
Testons l’adaptateur(5/7)
public class TestAdapter{
public static void main (String arg[])
{
Duck mallard= new MallardDuck();
Turkey wild = new WildTurkey();
Duck turkeyAdapter = new TurkeyAdapter(wild);
Duck tab[] = new Duck[2];
tab[0] = mallard;
tab[1] = turkeyAdapter ;
for(int i =0; i<2;i++)
{
tab[i].quack();
tab[i].fly();
}

}
}
 Donner le résultat d’exécution de cette classe

141
Riadh BEN HALIMA [Design patterns]
Le patron adapter (6/7)

 Définition: Adapter
 Le patron Adapter convertit l’interface d’une classe à
une autre interface que le client attend. Les adaptateurs
permettent aux classes, aillant des interfaces incompatibles,
de travailler ensemble.

142
Riadh BEN HALIMA [Design patterns]
Adapter :
Le diagramme de classes du patron (7/7)

<<interface>>
Client Target
request() L’Adapter implémente
l’interface Target

Le client voit seulement


Adapter Adaptee
l’interface Target.
request() specificRequest()

L’adapter est composé Toutes les requêtes sont


d’un Adaptee déléguées à Adaptee

143
Riadh BEN HALIMA [Design patterns]
Le patron
"Command"

144
Riadh BEN HALIMA [Design patterns]
Home-Automation :
Spécification (1/28)
 Objectif: Mettre en œuvre un système de contrôle distant
d’un ensemble d’appareils dans une maison
 Besoin: programmer les fonctionnalités d’un contrôleur
distant (avec 7 slots) selon des classes (prédéfinies par le
vendeur) de gestion des appareils installés dans la maison.
 Conception: OO
 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..

145
Riadh BEN HALIMA [Design patterns]
Home-Automation :
Analyse du contrôleur distant (2/28)

Il y a des boutons on et off


pour chaque slot

Ces deux boutons ont utilisés


pour contrôler la télévision
Il y a 7 slots à programmer.
On met un appareil différent Ces deux boutons ont utilisés
dans chaque slot, et on le pour contrôler la chaine Stéreo,
contrôle à travers les boutons etc…

Un bouton global UNDO


pour annuler l’action du
dernier bouton activé
Contrôleur distant

146
Riadh BEN HALIMA [Design patterns]
Home-Automation :
Les classes du vendeur (3/28)
 Les classes du vendeur nous donnent une idée sur les fonctionnalités
des appareils installés dans la maison :
Faucet AirConditioner
Stereo Appliance
openValue() setTemperature()
on() on()
off() closeValue()
off()
setCD()
Security
setRadio()
setVolume() Light GarageDoor arm()
on() up() desarm()
off() down()
stop()
GardenLight lightOn() OutDoorLight
CeilingLight lightOff() on()
setDuskTime()
setDawnTime() on() off()
manualOn() off()
dim() Sprinkler
manualOff()
waterOn()
waterOff()
147
Riadh BEN HALIMA [Design patterns]
Home-Automation :
Discussion de la conception (4/28)
 On s’attendait à des classes avec des méthodes on()-off()
pour bien correspondre avec le contrôleur distant
 C’est important de voir ça comme séparation des
préoccupation : le contrôleur doit savoir comment
interpréter l’appui sur le bouton et créer des requêtes, mais
il ne doit pas connaitre beaucoup sur les appareils et leurs
manières de fonctionnement (comment allumer une lampe)
 En d’autre terme, le contrôleur émet des requêtes génériques
 Une entité prendra en charge la transformation de cette requête
en action

148
Riadh BEN HALIMA [Design patterns]
Command :
Commander un dîner (5/28)
Order

 Le client donne sa
commande à la serveuse

Customer Waitress

 La serveuse récupère la
commande et la met sur le
comptoir, et la lance

 Le cuisinier prépare le
repas selon la commande
Order-Cook
149
Riadh BEN HALIMA [Design patterns]
Command :
Etudes des interactions (6/28) Je veux
un burger,
un shake et
createOrder() des frites

Il y a toutes les
instructions
nécessaires pour takeOrder()
préparer le repas

La serveuse récupère la
orderUp()
commande et la met sur le
comptoir, et la lance

Le cuisinier suit
makeBurger(), makeShake(), makeShips()
les instructions
de la commande

output()
150
Riadh BEN HALIMA [Design patterns]
Command :
Les rôles et les responsabilités (7/28)
 La commande (papier) est une requête pour préparer le repas
 S’il s’agit d’un objet, il peut être passé de la serveuse au comptoir
 Son interface consiste à une seule méthode orderUp(), qui encapsule les actions
nécessaires pour préparer le repas
 La serveuse n’a besoin de savoir comment préparer le repas!
 La tâche de la serveuse est de prendre la commande et d’invoquer
la méthode orderUp() dessus
 La méthode takeOrder() de la serveuse peut être paramétrée avec différentes
commandes de plusieurs clients. Ceci ne la dérange pas car elle sait que orderUp()
supporte sa commande
 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
nécessaires pour créer le repas
 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

151
Riadh BEN HALIMA [Design patterns]
Command :
Du dîner vers le patron Commande (8/28)
Le client crée la commande.
public void execute(){ action1() La commande consiste à un
L’objet Command offre une receiver.action1(); action2()
ensemble d’actions et un receveur
…..
seule méthode execute() receiver.action2();
}
qui encapsule les actions create
createCommandObject( ) Command
Object
execute() 

setCommand()
  Plus tard, le client
demande à l’invoker
setCommand()
d’exécuter sa commande
execute()
execute()

de ceci résulte
action1(), action2() action1() l’invocation des
L’invoker appelle la méthode action2() actions sur le
execute() de l’objet Command ….. Receiver
152
Riadh BEN HALIMA [Design patterns]
Command :
Correspondance (9/28)
Dîner Command Pattern

Serveuse Command
(Waitress)
Cuisinier execute()
(Order-Cook)
orderUp() Client

Commande invoke
(Order)
Client Receiver
(Customer)
takeOrder() setCommand()
153
Riadh BEN HALIMA [Design patterns]
Command :
Le patron Command (10/28)

 Définition: Command
 Le patron Command encapsule une requête comme un
objet, ainsi il nous permet de paramétrer d’autres objets
avec différentes requêtes, files d’attentes ou longues
requêtes, et supporte l’annulation d’une opération

154
Riadh BEN HALIMA [Design patterns]
Home-Automation :
Des Commandes (11/28)
Une requête encapsulée

action() execute()
execute()

execute(){
receiver.action()
}
execute() execute()

155
Riadh BEN HALIMA [Design patterns]
Command :
Le diagramme de classes du patron (12/28)
L’invoker tient la commande et Interface de tous les commandes.
Le client est responsable de
à un certain moment demande La commande est invoquée à travers
créer une ConcretCommand
à la commande de réaliser une sa méthode exécute, qui demande
et affecter son Receiver
requête en exécutant sa au Receiver de réaliser des actions
méthode execute()

Client <<interface>>
Invoker
Command
setCommand()
execute()
buttonWasPressed()

Receiver ConcretCommand
action() execute()

Le Receiver connait comment réaliser le public void execute(){


travail demandé afin de satisfaire la requête. receiver.action();
Le ConcretCommand définit une lisaison }
156
entre une action et un Receiver
Riadh BEN HALIMA [Design patterns]
Home-Automation :
Notre premier objet Commande (13/28)
 Implémentons l’interface Command
public interface Command{
void execute();
}

 Implémentons une Commande pour allumer la lumière


Light
on()
off()
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light){
this.light = light;
}
@Override
public void execute(){
light.on();
}
}
157
Riadh BEN HALIMA [Design patterns]
Home-Automation :
Notre premier objet Commande (14/28)
 Utilisons l’objet Commande

public class SimpleRemoteControl{


private Command slot;

public SimpleRemoteControl() {}

public void setCommand(Command command ){


slot=command;
}

public void buttonWasPressed(){


slot.execute();
}
}

158
Riadh BEN HALIMA [Design patterns]
Home-Automation :
Notre premier objet Commande (15/28)
 Testons la fonctionnalité du contrôleur distant

public class RemoteControlTest{


public static void main(String argv[]) {
SimpleRemoteControl remote = new SimpleRemoteControl();
Light light=new Light();
LightOnCommand lightOn=new LightOnCommand (light);

remote.setCommand(lightOn); //passer la commande à l’invoker


remote.buttonWasPressed(); //simuler l’appui sur le bouton

}
}

159
Riadh BEN HALIMA [Design patterns]
Home-Automation :
2ème Commande (16/28)
 Développer la classe GarageDoorOpenCommand
GarageDoor
up()
down()
stop()
lightOn()
lightOff()

public class GarageDoorOpenCommand implements Command {


private GarageDoor garageDoor;
public GarageDoorOpenCommand (GarageDoor garageDoor){
this.garageDoor = garageDoor;
}
@Override
public void execute(){
garageDoor.up();
garageDoor.lightOn();
}
}
160
Riadh BEN HALIMA [Design patterns]
Home-Automation :
Une autre Commande (17/28)
 Ajoutant cette commande au slot du contrôleur distant
public class RemoteControlTest{
public static void main(String argv[]) {
SimpleRemoteControl remote = new SimpleRemoteControl();
Light light = new Light();
LightOnCommand lightOn=new LightOnCommand(light);

GarageDoor garageDoor = new GarageDoor();


GarageDoorOpenCommand garageOpen = new
GarageDoorOpenCommand(garageDoor);

remote.setCommand(lightOn); //encapsuler la commande


remote.buttonWasPressed(); //allumer la lumière

remote.setCommand(garageOpen); //encapsuler la commande


remote.buttonWasPressed(); //ouvrir la porte du garage

161
Riadh BEN HALIMA [Design patterns]
Home-Automation :
Le contrôleur distant (18/28)
execute()
execute()

Light
Garage
Door execute()
Stereo
execute()

execute()

execute()

Les actions de la méthode execute()


sont invoquées sur le Receiver

L’invoker Contrôleur distant


off()
on()
162
Riadh BEN HALIMA [Design patterns]
Home-Automation :
Le diagramme de classes (19/28)

Le client <<interface>>
RemoteControl
Command
onCommands execute()
offCommands
RemoteLoader
setCommand()
onButtonWasPressed()
offButtonWasPressed()

Light LightOnCommand
on() execute()
off()
LightOffCommand
execute() public void execute(){
light.on();
public void execute(){
}
163 light.off();
} Riadh BEN HALIMA [Design patterns]
Home-Automation :
Programmer le contrôleur distant (20/28)
public class RemoteControl{
private Command onCommands[];
private Command offCommands[];
public RemoteControl() {
onCommands = new Command[7];
offCommands = new Command[7];
Command noCommand = new NoCommand(); Eviter la gestion de null
for(int i=0;i<7;i++){
onCommands[i] = noCommand;
offCommands[i] = noCommand;
} }
public void setCommand(int slot, Command onCommand,Command offCommand ){
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
public void onButtonWasPressed(int slot){
onCommands[slot].execute();
}
public void offButtonWasPressed(int slot){
offCommands[slot].execute();
}
@Override
public String toString(){
String s="";
for(int i=0;i<7;i++){
s+="Slot["+i+"] "+onCommands[i].getClass().getName() +"
"+offCommands[i].getClass().getName()+"\n";
} return s;
}} 164
Riadh BEN HALIMA [Design patterns]
Home-Automation :
Programmer les commandes (21/28)
 Programmer les classes suivantes:
 Light.java, Stereo.java
 LightOnCommand,.java LightOffCommand.java,
StereoOnWithCDCommand.java, StereoOffCommand.java
public class LightOnCommand public class StereoOnWithCDCommand
implements Command { implements Command {
private Light light; private Stereo stereo;
public LightOnCommand(Light public StereoOnWithCDCommand(Stereo
light){ stereo){
this.light = light; this.stereo = stereo;
} }
@Override @Override
public void execute(){ public void execute(){
light.on(); stereo.on();
} stereo.setCD();
} stereo.setVolume(11);
}
}
165
Riadh BEN HALIMA [Design patterns]
Home-Automation :
Tester le contrôleur (22/28)
public class RemoteLoader{
public static void main(String arg[]){
RemoteControl remoteControl = new RemoteControl();
Light livingRoomLight=new Light();
LightOnCommand livingRoomLightOnCommand = new
LightOnCommand(livingRoomLight);
LightOffCommand livingRoomLightOffCommand = new
LightOffCommand(livingRoomLight);
remoteControl.setCommand(0, livingRoomLightOnCommand,livingRoomLightOffCommand);
remoteControl.onButtonWasPressed(0);
remoteControl.offButtonWasPressed(0);
Stereo stereo = new Stereo();
StereoOnWithCDCommand stereoOnWithCDCommand = new
StereoOnWithCDCommand(stereo);
StereoOffCommand stereoOffCommand = new StereoOffCommand(stereo);
remoteControl.setCommand(1,stereoOnWithCDCommand,stereoOffCommand);
remoteControl.onButtonWasPressed(1);
remoteControl.offButtonWasPressed(1);
System.out.println(remoteControl.toString());
}
}
166
Riadh BEN HALIMA [Design patterns]
Home-Automation :
Undo: Annuler la dernière opération (23/28)
 Implémentons l’interface Command
public interface Command{
void execute();
void undo();
}
 Implémentons une Commande pour allumer la lumière
public class LightOnCommand implements Command { Light
private Light light;
on()
public LightOnCommand(Light light){
off()
this.light = light;
}
@Override
public void execute(){
light.on();
} Opération à exécuter en cas
@Override d’annulation de cette commande
public void undo(){
light.off();
}
167 }
Riadh BEN HALIMA [Design patterns]
Home-Automation :
Undo: Annuler la dernière opération (24/28)
public class RemoteControl{
private Command onCommands[];
private Command offCommands[];
private Command undoCommand; C’est ou on stockera la dernière
public RemoteControl() { commande exécutée pour le bouton undo
onCommands = new Command[7];
offCommands = new Command[7];
Command noCommand = new NoCommand();
for(int i=0;i<7;i++){
onCommands[i] = noCommand;
offCommands[i] = noCommand;
} Initialisation à NoCommand afin
undoCommand = noCommand; } d’éviter le traitement de null
public void onButtonWasPressed(int slot){
onCommands[slot].execute();
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
} 168
Riadh BEN HALIMA [Design patterns]
Home-Automation :
Tester le contrôleur avec UNDO (25/28)
Annuler l’allumage de la lumière
class RemoteLoader{
public static void main(String arg[]){
//…
remoteControl.onButtonWasPressed(0);
remoteControl.undoButtonWasPressed();

//…

remoteControl.onButtonWasPressed(1);
remoteControl.offButtonWasPressed(1);
System.out.println(remoteControl.toString());
}
}

169
Riadh BEN HALIMA [Design patterns]
Home-Automation :
La Macro-Commande (26/28)
 C’est le regroupement de plusieurs commandes en une seule
public class MacroCommand implements Command {
private Command [] commands; Stocker les commandes
public MacroCommand (Command [] commands){ de la macro-commande
this.commands = commands;
}
Un boucle pour exécuter
@Override
toutes les commandes de la
public void execute(){
macro-commande
for (int i=0;i<commands.length;i++)
commands[i].execute();
}
}
 Créer les commandes à mettre dans la macro-commande
LightOnCommand lightOnCommand = new LightOnCommand(light);
StereoOnWithCDCommand stereoOnWithCDCommand = new
StereoOnWithCDCommand(stereo);
TVOnCommand tvOnCommand = new TVOnCommand(tv);
Créer les commandes
//créer aussi les Off-Commandes

170
Riadh BEN HALIMA [Design patterns]
Home-Automation :
MacroCommand (27/28)
 Créer les macro-commandes

Command [] on = {lightOnCommand, stereoOnWithCDCommand, tvOnCommand};


Command [] off = {lightOffCommand, stereoOffCommand, tvOffCommand};

Les commandes sous


MacroCommand onMacro= new MacroCommand(on); formes de tableau
MacroCommand offMacro= new MacroCommand(off);

 Affecter les macro-commandes à un bouton


remoteControl.setCommand(2, onMacro, offMacro);
Affecter les macro-commandes
 Exécuter la macro-commande au bouton du slot n°2
System.out.println("Macro On");
remoteControl.onButtonWasPressed(2);
System.out.println("Macro Off"); Tester ces macro-commandes et
remoteControl.offButtonWasPressed(2); donner le résultat de l’exécution

171
Riadh BEN HALIMA [Design patterns]
Autre utilisation du patron Command :
Organiser les queues de requêtes (28/28)
 Les commandes nous offrent une manière de execute()
paquetage les morceaux de calcul (computation).
Les calculs (commandes créées par des applications)
execute()
seront placés dans des queues (queue de jobs) pour
être exécutés. execute()

execute()

C’est une manière efficace


pour limiter le calcul à un
execute() nombre fixé de Threads

Les threads récupèrent les commandes


de la queue une à une, et appellent leur
méthode execute(). Une fois complété,
execute() execute()
ils retournent pour une nouvelle
commande.

172
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 pour des interfaces
 Opter pour une conception faiblement couplée
 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
 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.
 Abstract Factory: offre une interface de création de familles d’objets
 Factory Method: définit une interface de création des objets
 Singleton: assure à une classe une seule instance
 Command: encapsule une requête comme un objet
173
Riadh BEN HALIMA [Design patterns]
Récapitulatif (2/2)
 Le patron Command découple un objet, faisant des requêtes, de l’objet
qui sait comment la réaliser
 Un objet Command est au centre de ce découplage et encapsule le
Receiver avec une action (ou des actions)
 L’Invoker exécute la requête d’un objet Command en appelant sa
méthode execute(), qui invoque les actions sur le Receiver
 Le patron Command supporte l’annulation (undo) par l’implémentation
d’une méthode undo() -dans la commande- qui restore l’ancien état du
système (avant l’exécution de la méthode execute())
 Les Macro-Commandes sont des simples extensions de Command qui
permettent linvocation de multiple commandes. Pareil, les macro-
commandes peuvent supporter les méthodes d’annulation (undo).
 Le patron Command est utilisé aussi pour implémenter l’organisation
des requêtes (Jobs) et la gestion de la journalisation (logging)

174
Riadh BEN HALIMA [Design patterns]
Exercice 1 (1/2)
 On vous demande de participer à la création d’un nouvel outil graphique. Cet
outil permettra de créer de manière intuitive des dessins avec un niveau de
complexité plus ou moins élevé. Les dessins pourront être composés d’un
ensemble de points, droites, arc de cercles ou autres formes simples telles que
des cercles ou des polygones. Cet outil sera similaire au programme appelé
«Paint» sous l’environnement Windows. La figure suivante présente un
diagramme de classes simplifié pour cette application :

 Vous êtes chargé de concevoir le mécanisme


Application
qui permettra de garder une trace des actions
+ ouvrir(doc : Dessin) de l’utilisateur. Ce dernier pourra ainsi annuler
+ fermer()
+ sauvegarder() : boolean les dernières actions faites.

Dessin  Question: Faites les modifications nécessaires


+ ajouterPoint(position : Vecteur2D)
+ ajouterDroite(pt1 : Vecteur2D, pt2 : Vecteur2D) au diagramme de classes pour implanter le
+ ajouterCercle(rayon : float, centre : Vecteur2D)
+ ajouterPolygone(listePts : []Vecteur2D) patron Commande (voir mail dans la page
suivante).
175
Riadh BEN HALIMA [Design patterns]
Exercice 1 (2/2)
public static void main(String[] args) {
Dessin dessin = new Dessin("Dessin1");
Application application = new Application();
//invoker
Palette palette = new Palette();
//command ouvrir Application
OuvrirApplicationCommand c0 = new OuvrirApplicationCommand(application, dessin);
palette.setCommand(0, c0);
palette.buttonWasPressed(0);
//Command Point
Vecteur2D position = new Vecteur2D(1, 2);
AjouterPointDessinCommand c1 = new AjouterPointDessinCommand(dessin,position);
palette.setCommand(1, c1);
palette.buttonWasPressed(1);
//Command Polygone
Vecteur2D[] listePts ={ new Vecteur2D(2, 3), new Vecteur2D(4, 7), new Vecteur2D(1, 5),new Vecteur2D(1, 0) };
AjouterPolygoneDessinCommand c2 = new AjouterPolygoneDessinCommand(
dessin, listePts);
palette.setCommand(2, c2);
palette.buttonWasPressed(2);
//Command Sauvegarde
SauvegarderApplicationCommand c3 = new SauvegarderApplicationCommand(application);
palette.setCommand(3, c3);
palette.buttonWasPressed(3);
}

176
Riadh BEN HALIMA [Design patterns]
Exercice 2
 Le but de cet exercice est de tester la puissance du pattern Command. Pour ce
faire, nous disposons d’une calculatrice offrant les opérations arithmétiques de
base : +, -, /, * et racine carrée sur 2 réels et nous voulons transformer les
actions (des utilisateurs) de calculs sur cette calculatrice en des commandes.
 1. Donne le digramme de classes décrivant cette transformation avec le pattern
Command.
 2. Implanter ce diagramme tout en respectant le client suivant :
public class Client {
public static void main(String[] args) {
Calculatrice calculatrice =new Calculatrice(); Calculatrice
Command plusCommand =new PlusCommand(calculatrice, 1, 3);
+ plus(x: float, y: float): float
Command sousCommand =new SousCommand(calculatrice, 8, 12); + mult(x: float, y: float): float
Command divCommand=new DivCommand(calculatrice, 7, 4); + sous(x: float, y: float): float
Command multCommand=new MultCommand (calculatrice, 4, 5); + div(x: float, y: float): float
Command sqrtCommand=new SqrtCommand(calculatrice, 9); + sqrt(x: float): float
CalculatriceControl control =new CalculatriceControl();
control.setCommand(0, plusCommand); control.buttonPressed(0);
control.setCommand(1, sousCommand); control.buttonPressed(1);
control.setCommand(2, divCommand); control.buttonPressed(2);
control.setCommand(3, multCommand); control.buttonPressed(3);
control.setCommand(4, sqrtCommand); control.buttonPressed(4); } }
3. Implanter la macro commande qui permet de résoudre l’équation :
Command secondDegre = new MacroCommand(calculatrice, 4, 5, 1); //a = 4, b = 5 et c = 1
Avec :
177
Riadh BEN HALIMA [Design patterns]
Le patron
"Façade"

178
Riadh BEN HALIMA [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() ….
on()
on()
 allumer l’écran setAm() off()
off()
setFM() play()
setCD()
 démarrer l’ampli …
setDVD()
stop()
pause()
 démarrer le DvDplayer …

le brancher avec l’mpli
Screen
jouer le DvD
up()
 etc.. down()

179 Et bien d’autres classes


Riadh BEN HALIMA [Design patterns]
Le patron Facade:
Les classes d’un Home cinéma (2/4)
La Façade: une nouvelle Le client invoque les
classe pour le méthodes sur la Façade
WatchDVD()
HomeCinema avec peu HomeCinema
de méthode WatchTV()
WatchDVD()
….

Tuner Amplifier DVDPlayer


amplifier tuner amplifier
dvdPlayer
on() …. on()
off() off() La classe Façade traite les
setAm() on() play() composants du
setFM() off() stop() HomeCinema comme des
… setCD() pause() sous-systèmes qu’il invoque
setDVD() …

pour implémenter ses
CDPlayer méthodes
amplifier Projector
on() Screen on()
off() off()
up()
play()
down()
stop()
180
pause()
Riadh BEN HALIMA [Design patterns]
Le patron façade (3/4)

 Définition: Façade
 Le patron Façade présente une interface unifiée pour un
ensemble de sous-interfaces dans un système. La façade
définit une interface de haut niveau qui rend facile
l’utilisation du système.

181
Riadh BEN HALIMA [Design patterns]
Façade:
Le diagramme de classes du patron (4/4)
Une interface unifiée et
simple à utiliser

Client Façade

Un client heureux car


son travail devient
facile grâce à la façade Les classes des sous-systèmes

Systèmes complexes

182
Riadh BEN HALIMA [Design patterns]
Testons nos connaissances!

Patron Rôle

Decorator Convertit une interface en une


autre

Adapter Ne modifie pas l’interface,


mais ajoute des responsabilités

Facade Rend les interfaces simples

183
Riadh BEN HALIMA [Design patterns]
Le patron
"Builder"

184
Riadh BEN HALIMA [Design patterns]
Builder :

 Définition: Builder
 Le patron Builder (Monteur en français) sépare la
construction d'un objet complexe de sa représentation de
telle sorte que le même processus de construction peut
avoir différentes représentations.

185
Riadh BEN HALIMA [Design patterns]
Builder Construit un objet utilisant
la méthode du Builder
(builder.buildView())

Director Builder
builder : Builder
buildView() Classe abstraite qui
construct() contient le produit

ConcreteBuilder1 ConcreteBuilder2 Product


buildView() buildView()
getResult(): Product getResult(): Product

Construit et assemble L'objet complexe en


les différentes parties cours de construction
des produits

186
Riadh BEN HALIMA [Design patterns]
Exemple d’utilisation
Objectif: préparer 2 configurations de voitures à vendre:
Couleur Rouge avec 4 roues && Couleur Noire avec 3 roues
CarDirector CarBuilder
builder : CarBuilder
buildView()
construct() getResult(): Car

RedCarBuilder BlackCarBuilder Car


buildView() buildView() wheels : int
color : String

car.setColor("Red");
car.setWheels(4); car.setColor("Black");
car.setWheels(3);
187
Riadh BEN HALIMA [Design patterns]
Builder vs autre patterns

 Builder vs Factory
 Factory : création d’un seul objet parmi plusieurs
 Builder : création de plusieurs « objet » (ou objet
complexe ) par une seule méthode
 Builder vs Decorator
 Avec le Decorator, il est difficile de gérer tous les objets
créés et de les décorer
 Avec le Builder, les combinaisons de « décoration » sont
pré-préparées
 S’inspirant du service du restaurant:
 A la carte : Builder
 Self-Service : Decorator

188
Riadh BEN HALIMA [Design patterns]
Le patron
"Iterator"

189
Riadh BEN HALIMA [Design patterns]
Iterator :

 Définition: Iterator
 Le patron Iterator est utilisé pour obtenir un moyen
d'accéder aux éléments d'une collection d'objets de
manière séquentielle sans qu'il soit nécessaire de
connaître sa représentation.

190
Riadh BEN HALIMA [Design patterns]
Iterator :

« interface »
Iterator
hasNext(): boolean
next(): Object

SpecificIterator
hasNext(): boolean
next(): Object

191
Riadh BEN HALIMA [Design patterns]
Exemple d’utilisation
Objectif: Parcours d’un tableau de chaines avec un itérateur

192
Riadh BEN HALIMA [Design patterns]
Le patron
"Composite"

193
Riadh BEN HALIMA [Design patterns]
Composite :

 Définition: Composite
 Le patron Composite permet de manipuler un
groupe d'objets de la même façon que s'il s'agissait
d'un seul objet. L’objet composite est constitué d'un
ou de plusieurs objets similaires.

194
Riadh BEN HALIMA [Design patterns]
Composite :
*
Component

operation()
add(Component)
//autres méthodes

Leaf Composite Children

operation() operation()
//autres méthodes add(Component) for each g in children
//autres méthodes g.operation();

195
Riadh BEN HALIMA [Design patterns]
Exemple d’utilisation
Objectif: Représenter le contenu d’un disque dur ou d’un arbre

196
Riadh BEN HALIMA [Design patterns]
Exercice : Articles à vendre. Design
pattern "composite".
Une société vend des articles de papeterie. On se limite aux articles suivants:
• Stylos décrits par une référence, un nom (par exemple "stylo noir B2"), une
marque, un prix unitaire et une couleur,
• ramettes de papier décrites par une référence, un nom, une marque, un prix
unitaire et le grammage du papier (par exemple, 80 g/m²).
De plus cette société peut vendre ces articles par lots. On suppose que les lots
sont composés d'un certain nombre d'un même type d'articles, par exemple un
lot de 10 stylos noirs B2 de la marque BIC. Un lot a une référence et une
marque (celle de l'article). Le nom du lot est déterminé par le nombre d'articles
dont il est composé ; par exemple "Lot de 10 stylo noir B2". Le prix du lot est
déterminé par le prix unitaire de l'article multiplié par le nombre d'articles,
auquel est enlevé un certain pourcentage qui dépend du lot. Par exemple, si un
lot de 10 stylos à 5 dinars a un pourcentage de réduction de 20 %, le prix du lot
sera de 5 x (100 - 20) / 100 = 4 dinars. Ecrivez des classes pour représenter les
différents articles et testez le tout dans un main.

197
Riadh BEN HALIMA [Design patterns]
Le patron
"Proxy"

198
Riadh BEN HALIMA [Design patterns]
Proxy :

 Définition: Proxy
 Le patron Proxy fournit un substitut ou un espace réservé
pour un autre objet pour contrôler l'accès ou supporter
la distribution.
 Il permet de supporter les objets gourmant en
ressources, et qu’on ne veut pas instancier jusqu'à ce
qu'ils soient effectivement demandés par le client.

199
Riadh BEN HALIMA [Design patterns]
Proxy :

Component

request()
//autres méthodes

RealSubject Proxy

request() request()
//autres méthodes //autres méthodes If (realSubject == null){
realSubject = new RealSubject();
}
realSubject.request();

200
Riadh BEN HALIMA [Design patterns]
Exemple d’utilisation
Objectif: Un contrôle d’accès du cash via le proxy chèque

201
Riadh BEN HALIMA [Design patterns]
Le patron
"State"

202
Riadh BEN HALIMA [Design patterns]
State:

 Définition: State
 Le patron State cherche principalement à séparer un objet
de ses états/comportements en encapsulant ces derniers dans
des classes à part.
 Ces états/comportement, qui sont généralement des
singletons, sont interchangeables pour refléter l’état de l’objet

203
Riadh BEN HALIMA [Design patterns]
State :

Context
<<interface>>
currentState : State State
goNext() goNext(context)
setCurrentState(state)

currentState.goNext(this);

StateOne StateTwo StateThree


goNext(context) goNext(context) goNext(context)

context.setCurrentState(StateTwo);

Généralement, les états sont des singletons

204
Riadh BEN HALIMA [Design patterns]
Exemple d’utilisation
Objectif: Représenter les 2 états d’un distributeur : vente/chargement

205
Riadh BEN HALIMA [Design patterns]
Le patron
"Bridge"

206
Riadh BEN HALIMA [Design patterns]
Bridge:

 Définition: Bridge
 Le patron Bridge découple une abstraction de son
implémentation afin que les 2 varient indépendamment
 Le patron Bridge est utile lorsque la classe et ce quelle fait,
varie couramment.

207
Riadh BEN HALIMA [Design patterns]
Bridge :

208
Riadh BEN HALIMA [Design patterns]
Exemple d’utilisation
Objectif: L’interrupteur fonctionne avec abstraction de la cible

209
Riadh BEN HALIMA [Design patterns]
Le Modèle-Vue-
Contrôleur

210
Riadh BEN HALIMA [Design patterns]
Architecture Modèle/Vue/Contrôleur
 Le modèle MVC: destiné à répondre aux besoins des applications
interactives en séparant les problématiques liées aux différents
composants au sein de leur architecture respective.
 Ce paradigme regroupe les fonctions en trois catégories :
 un modèle (modèle de données),
 une vue (présentation, interface utilisateur)
 un contrôleur (logique de contrôle, gestion des événements, synchronisation)

L'idée est de bien séparer les


données, la présentation et les
traitements.

211
Riadh BEN HALIMA [Design patterns]
Le Modèle

 Le modèle représente le cœur (algorithmique) de l'application :


traitements des données, interactions avec la base de données, etc.
 décrit les données manipulées par l'application.
 regroupe la gestion de ces données et est responsable de leur intégrité.
 La base de données sera l'un de ses composants.
 Le modèle comporte des méthodes standards pour mettre à jour
ces données (insertion, suppression, changement de valeur).
 Les résultats renvoyés par le modèle ne s'occupent pas de la
présentation.
 Le modèle ne contient aucun lien direct vers le contrôleur ou la vue.
 Sa communication avec la vue s'effectue au travers du patron
Observateur.

212
Riadh BEN HALIMA [Design patterns]
La Vue

 Ce avec quoi l'utilisateur interagit se nomme précisément la vue.


 présenter les résultats renvoyés par le modèle.
 recevoir toute action de l'utilisateur (hover, clic de souris, sélection d'un bouton radio,
cochage d'une case, entrée de texte, de mouvements, de voix, etc.).
 Ces différents événements sont envoyés au contrôleur.
 La vue n'effectue pas de traitement,
 afficher les résultats des traitements effectués par le modèle et interagir avec
l'utilisateur.
 Plusieurs vues peuvent afficher des informations partielles ou non d'un même
modèle.

213
Riadh BEN HALIMA [Design patterns]
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
 reçoit tous les événements de l'utilisateur et enclenche les actions à
effectuer
 Si une action nécessite un changement des données, le contrôleur
demande la modification des données au modèle, et ce dernier notifie la
vue que les données ont changé pour qu'elle se mette à jour.
 D'après le patron de conception observateur/observable, la vue
est un « observateur » du modèle qui est lui « observable »
 Le contrôleur n'effectue aucun traitement, ne modifie aucune donnée.
 Il analyse la requête du client et se contente d'appeler le modèle adéquat et de
renvoyer la vue correspondant à la demande.

214
Riadh BEN HALIMA [Design patterns]
Flux de traitement
 lorsqu'un client envoie une requête à l'application :
 la requête envoyée depuis la vue est analysée par le contrôleur (par
exemple un clic de souris pour lancer un traitement de données) ;
 le contrôleur demande au modèle approprié d'effectuer les traitements et
notifie à la vue que la requête est traitée (via par exemple un callback) ;
 la vue notifiée fait une requête au modèle pour se mettre à jour (par exemple
affiche le résultat du traitement via le modèle).

215
Riadh BEN HALIMA [Design patterns]
Architecture MVC ou 3-Tier

 L'architecture trois tiers est un modèle en couches, c'est-à-dire, que chaque


couche communique seulement avec ses couches adjacentes
 le flux de contrôle traverse le système de haut en bas
 Dans le modèle MVC, la vue peut consulter directement le modèle (lecture)
sans passer par le contrôleur. Par contre, elle doit nécessairement passer
par le contrôleur pour effectuer une modification (écriture).
 le contrôleur peut alors envoyer des requêtes à toutes les vues de manière à ce
qu'elles se mettent à jour.
 La différence fondamentale se trouve dans le fait que l'architecture 3-Tier
sépare la couche métier de la couche accès aux données.
 Pour qu'une application MVC soit une vraie application 3-Tier il faut lui ajouter une
couche d'abstraction d'accès aux données de type DAO (Data Access Object).
 Inversement pour qu'une application 3-Tier respecte MVC il faut lui ajouter une
couche de contrôle entre la couche métier et la couche présentation.

216
Riadh BEN HALIMA [Design patterns]
Bibliographie
 Head First: Design Patterns

 https://sourcemaking.com/design_patterns/

 Design Patterns : Elements of Reusable Object-Oriented


Software (http://www.uml.org.cn/c++/pdf/DesignPatterns.pdf)

 Reconnaissance :
 Slim BenHammouda, Squeezer Software
 Wajdi Louati, ENIS

217
Riadh BEN HALIMA [Design patterns]

Vous aimerez peut-être aussi