Académique Documents
Professionnel Documents
Culture Documents
(Design Patterns)
Xavier Crégut
<Prenom.Nom@enseeiht.fr>
2 Patrons fondamentaux
3 Patrons créateurs
4 Patrons structurels
5 Patrons comportementaux
5 Patrons comportementaux
Historique
Motivation
Définition
Rubriques de la description
Classification
Utilisation
Historique
Motivation
Définition
Rubriques de la description
Classification
Utilisation
Historique
Motivation
Définition
Rubriques de la description
Classification
Utilisation
Historique
Motivation
Définition
Rubriques de la description
Classification
Utilisation
Nom Collaborations
Intention Conséquences
Alias
Implantation
Motivation
Exemples de code
Indications d’utilisation
Structure Utilisations remarquables
Constituants Patrons apparentés
Historique
Motivation
Définition
Rubriques de la description
Classification
Utilisation
Historique
Motivation
Définition
Rubriques de la description
Classification
Utilisation
5 Patrons comportementaux
Délégation
Interface
Classe abstraite
Interface et Classe abstraite
Immuable
Interface de marquage
Délégation
Interface
Classe abstraite
Interface et Classe abstraite
Immuable
Interface de marquage
Délégation
Interface
Classe abstraite
Interface et Classe abstraite
Immuable
Interface de marquage
Intention :
I Garantir que la logique commune à plusieurs classes est implantée de
manière cohérente pour chaque classe
I Éviter le code redondant (et les efforts de maintenance associés)
I Faciliter l’écriture de nouvelles classes implantant la même logique
Solution :
I Utiliser une classe abstraite pour factoriser le code commun implantant la
logique
I Les méthodes peuvent éventuellement être définies comme final
Exemple : La classe abstraite java.util.AbstractCollection définit
toutes les opérations de Collection sauf size et iterator.
I Collection concrète non modifiable : définir seulement size et iterator
I Collection concrète modifiable : définir aussi add (et remove sur l’iterator).
Exemple 2 : La classe TestCase de JUnit (cf Patron de méthode, T. 109)
Délégation
Interface
Classe abstraite
Interface et Classe abstraite
Immuable
Interface de marquage
Idée :
Pourquoi choisir entre Interface et Classe abstraite ?
Faire les deux !
Intérêts :
on cumule les avantages des deux patrons
le client (surtout en Java) est libre de choisir entre :
I réaliser l’interface (et dupliquer le code mais hériter d’une autre classe)
I hériter de la classe abstraite (récupérer le code) mais sans pouvoir hériter
d’une autre classe
Exemple : L’API des collections en Java :
Collection et AbstractCollection,
List et AbstractList, etc.
Délégation
Interface
Classe abstraite
Interface et Classe abstraite
Immuable
Interface de marquage
Intention :
I Augmenter la robustesse des objets partageant les mêmes objets
I Diminuer le coût de la gestion du parallélisme.
Intérêts :
I facilite la gestion des objets partagés (personne ne peut les modifier !)
I permet de réaliser une composition sans avoir à faire de copie des objets
I pas de besoin de synchonisation (lecture seule)
I MAIS toute « modification » nécessite la création d’un nouvel objet
Exemple :
I String (immuable) par opposition à StringBuffer (modifiable).
Délégation
Interface
Classe abstraite
Interface et Classe abstraite
Immuable
Interface de marquage
1
sérialiser : encoder l’information en mémoire sous forme d’octets ou autres pour la
sauvegarder (persistance), la transporter à travers un réseau, etc.
Xavier Crégut (ENSEEIHT) Patrons de conception(Design Patterns) 33 / 128
Sommaire
Monteur
2 Patrons fondamentaux
Fabrique
Fabrique abstraite
3 Patrons créateurs
Prototype
Singleton
4 Patrons structurels
5 Patrons comportementaux
Monteur
Fabrique
Fabrique abstraite
Prototype
Singleton
Intention
Dissocier la construction d’un objet complexe de sa représentation, de
sorte que le même processus de construction permette des
représentations différentes.
Alias : —
Indications d’utilisation
I l’algorithme de création d’un objet complexe doit être indépendant des
parties qui composent l’objet et de la manière dont ces parties sont
agencées
I le processus de construction doit autoriser des représentations différentes
de l’objet en construction
Exemple :
I Créer un message électronique à partir du destinataire, de l’expéditeur, de
l’objet, du texte, des fichiers attachés, etc.
Directeur Monteur
construire construirePartie
MessageBuilder
Exemple : Créer un message
setTo(String)
électronique (Produit) en précisant le
setObject(String)
destinaire, l’objet, le texte, les fichiers
...
attachés, etc. (construire les parties).
getMessage(): Message
Monteur
Fabrique
Fabrique abstraite
Prototype
Singleton
Créateur
CréateurConcret
fabriquer(): Product ProductConcret
Monteur
Fabrique
Fabrique abstraite
Prototype
Singleton
Intention
Fournir une interface pour la création de familles d’objets apparentés
ou interdépendants, sans qu’il soit nécessaire de spécifier leurs classes
concrètes.
Alias : Kit
Indications d’utilisation
I un système doit être indépendant de la manière dont ses produits ont été
construits, combinés ou représentés
I un système est composé à partir d’une famille de produits, parmi plusieurs
I on souhaite renforcer le caractère de communauté d’une famille de
produits conçus pour être utilisés ensemble
Exemples : boîte à outils proposant plusieurs « look-and-feel »
(décors).
Solution (principe) : définir une interface Fabrique qui déclare les
méthodes créerAscenceur, créerFenêtre, etc.
FabriqueGUI Client
créerBouton()
créerFenêtre() Bouton
FabriqueMotif
BoutonMotif BoutonWin
créerBouton()
créerFenêtre()
FabriqueWin Fenêtre
créerBouton()
créerFenêtre()
FenêtreMotif FenêtreWin
FabriqueAbstraite Client
créerProduitA()
créerProduitB() ProduitAbstraitA
FabriqueConcrète1
ProduitA1 ProduitA2
créerProduitA()
créerProduitB()
FabriqueConcrète2 ProduitAbstraitB
créerProduitA()
créerProduitB()
ProduitB1 ProduitB2
Monteur
Fabrique
Fabrique abstraite
Prototype
Singleton
Intention
Spécifie le type des objets à créer par une instance (le prototype) et
crée de nouveaux objets en copiant ce prototype (clonage).
Alias : —
Indications d’utilisation
I classes à instancier spécifiées à l’exécution (p.ex. chargement
dynamique).
I éviter de construire une hiérachie de classes Fabrique
I classes pouvant prendre un nombre réduit d’états (un prototype par état)
Exemples :
I Éditeur de musique : les notes sont disponibles et copiées avant utilisation
I Éditeur UML : les éléments apparaissent dans un menu et sont copiés pour
les ajouter sur le dessin (puis les adapter).
p = prototype.clone()
PrototypeConcret1 PrototypeConcret2
clone() clone()
Monteur
Fabrique
Fabrique abstraite
Prototype
Singleton
Intention
Garantir qu’une classe n’a qu’une seule instance et
fournir un point d’accès global à cette instance.
Alias : —
Indications d’utilisation
I Il doit y avoir exactement une instance d’une classe
I Cette instance doit être accessible globalement
I Le type du singleton peut-être sous-classé
=⇒ la classe elle-même est responsable de gérer l’unicité de l’objet et
son accès
Exemples : gestionnaire de fenêtres, file d’impression, etc.
18 }
Définir la façon d’assembler des classes et des objets pour réaliser des
structures complexes.
Agrégat d’objets conçus comme des « macro-composants ».
Patrons présentés :
Adaptateur
Pont
Composite
Décorateur
Façade
Poids mouche
Procuration
Discussion
Adaptateur
Pont
Composite
Décorateur
Façade
Poids mouche
Procuration
Discussion
Intention
Convertit l’interface d’une classe en une autre conforme à l’attente
d’un client.
Permet de faire collaborer des classes aux interfaces incompatibles.
Alias : Empaqueteur (Wrapper)
Indications d’utilisation
I On veut utiliser une classe dont l’interface ne coïncide pas avec celle
escomptée
I Prévoir l’ajout de classes non encore connues
Exemples :
I Utiliser un ArrayList pour réaliser une Pile (Adaptation d’objet).
I Restructuration de Vector avec List (Adaptation de classe)
«implantation»
Adapteur
requêteSpécifique()
requête()
Adapteur
requêteSpécifique()
requête()
Adaptateur
Pont
Composite
Décorateur
Façade
Poids mouche
Procuration
Discussion
imp.opérationImpl()
ImplanteurConcret1 ImplanteurConcret2
AbstractionFine
opérationImpl() opérationImpl()
Adaptateur
Pont
Composite
Décorateur
Façade
Poids mouche
Procuration
Discussion
Intention
I Compose des objets en des structures arborescentes pour représenter des
hiérarchies composant/composé.
I Permet au client de traiter uniformément un objet individuel ou les
combinaisons de ceux-ci
Alias : —
Indications d’utilisation
I représentation de structures récursives d’éléments hétérogènes
I traitement uniforme d’un objet individuel ou d’une combinaison d’objets
Exemples : Groupe dans éditeur de schémas mathématiques.
Conséquences :
+ facilite l’ajout de nouveaux types de composants
– difficile d’imposer des contraintes sur les compositions possibles
Composant
enfant
Client opération()
0..*
getEnfant(int)
ajouter(Composant)
supprimer(Composant)
Feuille Composite
opération()
opération() for (Composant c: enfants) {
getEnfant(int) c.opération()
ajouter(Composant)
}
supprimer(Composant)
Adaptateur
Pont
Composite
Décorateur
Façade
Poids mouche
Procuration
Discussion
Composant − leComposant
+ opération()
ComposantConcret Décorator
DécorateurConcretA DécorateurConcretB
+ opération() − otherAttribute
+ otherOperation() + opération()
: Test
« create »
comp : ComposantConcret
« create » (comp)
dec : DécorateurConcretA
opération()
opération()
JComponent
MyPanel Decorator
ScrollBarDecorator BorderDecorator
1 JComponent c =
− couleur: java::awt::Color
2 new BorderDecorateur(
+ BorderDecorator(component_: JComponent,
3 new ScrollBarDecorateur(
couleur_: java::awt::Color)
4 new MyPanel())); + setCouleur(couleur_: java::awt::Color)
+ getCouleur(): java::awt::Color
# paintComponent(g: Graphics)
Adaptateur
Pont
Composite
Décorateur
Façade
Poids mouche
Procuration
Discussion
Intention
Fournit une interface unifiée pour un sous-système, interface de haut
niveau rendant le système plus facile à utiliser (couplage réduit).
Alias : —
Indications d’utilisation
I On souhaite disposer d’une interface simple pour un système complexe
I diminuer le couplage entre un sous-système et les clients
I structuration d’un sous-système en niveaux
Exemple :
Un compilateur (compiler) qui utilise l’analyseur lexical, l’analyseur
syntaxique, l’analyseur sémantique, une table des symboles, engendre du
code pour plusieurs architectures, etc.
classes client
Façade
sous−système
Adaptateur
Pont
Composite
Décorateur
Façade
Poids mouche
Procuration
Discussion
Implantation :
I déportation des états extrinsèques
I gestion des objets partagés
FabriqueDePoidsMouche PoidsMouche
*
getPoidsMouche(touche) operation(etatExtrinseque)
si poidsMouche(touche) existe {
return poids mouche existant
} sinon {
créer un nouveau poids mouche
l’ajouter au parc à poids mouches
return nouveau poids mouche
}
PoidsMoucheConcret PoidsMoucheConcretNonPartagé
Client
étatIntrinsèque toutEtat
operation(etatExtrinseque) operation(etatExtrinseque)
Adaptateur
Pont
Composite
Décorateur
Façade
Poids mouche
Procuration
Discussion
Intention
Fournit à un client un mandataire ou un remplaçant pour contrôler l’accès
à un objet fournisseur
Alias : Subrogé (Surrogate), Mandataire, Procuration
Indications d’utilisation
I procuration à distance : représentant local d’un objet distant
I procuration virtuelle : création d’objet à la demande
I procuration de protection : contrôle les accès au fournisseur
I référence intelligente (smart pointer) : remplaçant d’un pointeur brut qui
ajoute compteur de références, chargement en mémoire d’un objet
persistant...
Sujet
Client
requête
...
SujetRéel Procuration
sujetRéel.requête()
requête sujetRéel
requête ...
Adaptateur
Pont
Composite
Décorateur
Façade
Poids mouche
Procuration
Discussion
Chaîne de responsabilités
1 Pourquoi les patrons ? Commande
Interpréteur
Itérateur
2 Patrons fondamentaux
Médiateur
Mémento
3 Patrons créateurs
Observateur
État
4 Patrons structurels
Stratégie
Patron de méthode
5 Patrons comportementaux
Visiteur
Chaîne de responsabilités
Commande
Interpréteur
Itérateur
Médiateur
Mémento
Observateur
État
Stratégie
Patron de méthode
Visiteur
Alias : —
Indications d’utilisation
I Une requête peut être gérée par plus d’un objet à la fois et le gestionnaire
n’est pas connu a priori (déterminé dynamiquement)
I On souhaite adresser une requête à plusieurs objets, sans spécifier
explicitement le récepteur
I Les objets qui traitent la demande doivent être définis dynamiquement
Exemples :
I même principe que les exceptions Java : récepteurs = catch
Conséquences :
I Réduction du couplage
I Souplesse accrue dans l’attribution des responsabilités
I Pas de garantie de traitement de la requête !
successeur
Gestionnaire
Client
gérerRequête
GestionnaireConcret1 GestionnaireConcret2
gérerRequête gérerRequête
Chaîne de responsabilités
Commande
Interpréteur
Itérateur
Médiateur
Mémento
Observateur
État
Stratégie
Patron de méthode
Visiteur
Intention
I encapsuler une requête (ou un traitement) comme un objet (version objet
des callbacks)
I paramétrer le contexte client
I gestion possible des requêtes/traitements en FIFO, annulation possible.
Alias : Action, Transaction
Indications d’utilisation :
I réaliser un principe de callback
I permettre de mémoriser des modifications
I structurer le système en opérations de haut niveau (transaction)
I voir aussi Intention
Invocateur Commande
exécuter()
Client
Récepteur CommandeConcrète
action() exécuter() récepteur−>action()
état
Chaîne de responsabilités
Commande
Interpréteur
Itérateur
Médiateur
Mémento
Observateur
État
Stratégie
Patron de méthode
Visiteur
Intention
Définit une représentation de la grammaire d’un langage simple ainsi
qu’un interpréteur.
Alias : —
Indications d’utilisation
I la grammaire du langage doit être simple
I l’efficacité n’est pas un souci majeur
Exemples : Évaluer une expression arithmétique
Conséquences :
I les grammaires complexes sont difficiles à maintenir
=⇒ utiliser des générateurs de compilateur (javacc, java_cup, antlr, etc.)
Chaîne de responsabilités
Commande
Interpréteur
Itérateur
Médiateur
Mémento
Observateur
État
Stratégie
Patron de méthode
Visiteur
Intention
Fournit un moyen pour accéder séquentiellement à chacun des éléments
d’un agrégat d’objets sans révéler la représentation interne de l’agrégat
Alias : Curseur (Cursor)
Indications d’utilisation
I accéder aux éléments d’un agrégat sans révéler sa structure interne
I gérer simultanément plusieurs parcours sur des agrégats
I offrir une interface uniforme pour parcourir différents types d’agrégats
Exemples :
I Les itérateurs des collections Java
Conséquences :
I possibilité de définir plusieurs parcours (infixe et préfixe par exemple)
I simplification de l’interface de l’agrégat
I parcours simultanés possibles sur un même agrégat
<<utilise>>
<< interface >> OutilsListe
Itérateur
«interface» somme(liste: Liste): double
Liste
suivant(): T
encore(): boolean
ItérateurTab
ListeTab
suivant(): T suivante 0..1
encore(): boolean
ItérateurChainé Cellule
ListeChaînée − première
élément: double 0..1
suivant(): T 0..1
encore(): boolean
Chaîne de responsabilités
Commande
Interpréteur
Itérateur
Médiateur
Mémento
Observateur
État
Stratégie
Patron de méthode
Visiteur
médiateur
Médiateur Collègue
Chaîne de responsabilités
Commande
Interpréteur
Itérateur
Médiateur
Mémento
Observateur
État
Stratégie
Patron de méthode
Visiteur
Chaîne de responsabilités
Commande
Interpréteur
Itérateur
Médiateur
Mémento
Observateur
État
Stratégie
Patron de méthode
Visiteur
Intention
Définit une interdépendance de type un à plusieurs de sorte que quand un
objet change d’état, tous ceux qui en dépendent en soient avertis et
automatiquement mis à jour
Alias : Dépendants, Diffusion–Souscription (Publish–Subscribe)
Indications d’utilisation
I Pour faciliter la réutilisation de deux représentations inter-dépendantes
d’un même concept
I Quand la modification d’un objet nécessite de modifier un nombre
indéterminé d’autres objets
I Quand un objet doit notifier d’autres objets sans faire d’hypothèses sur la
nature de ces objets (faible couplage)
Exemples : Plusieurs vues sur un même modèle
Sujet Observateur
observateurs
+ inscrire (o : Observateur) 0..* + mettreAJour()
+ annuler (o : Observateur)
# avertir()
for(Observateur s: observateurs) {
o.mettreAJour();
}
SujetConcret ObservateurConcret
sujet
getEtat()
mettreAJour
etat
Observable Observateur
observateurs
+ inscrire (o : Observateur) 0..* + mettreAJour()
+ annuler (o : Observateur)
# avertir()
2 *
Point Segment
− extremite1,
− extremite2
Chaîne de responsabilités
Commande
Interpréteur
Itérateur
Médiateur
Mémento
Observateur
État
Stratégie
Patron de méthode
Visiteur
état État
Contexte
gérer()
requete()
état.gérer()
ÉtatConcretA ÉtatConcretB
gérer() gérer()
Chaîne de responsabilités
Commande
Interpréteur
Itérateur
Médiateur
Mémento
Observateur
État
Stratégie
Patron de méthode
Visiteur
Intention
I Définit une famille d’algorithme, encapsule chacun d’entre eux et les rend
interchangeables.
I Permet aux algorithmes d’évoluer indépendamment de leurs clients
Alias : Politique
Indications d’utilisation
I Plusieurs classes apparentées ne différent que par leur comportement
I On a besoin de diverses variantes d’un algorithme.
I Un algorithme utilise des données que les clients n’ont pas à connaître
(masquer les structures complexes)
I Une classe définit de nombreux comportements figurant dans des
conditionnelles multiples : faire autant de classes Stratégie
Exemples :
I Gérer les coupures de fin de ligne
I trier les éléments d’une collection
stratégie Stratégie
Contexte
interfaceContexte() interfaceAlgorithme()
stratégie.interfaceAlgorithme()
StratégieConcrèteA StratégieConcrèteB
interfaceAlgorithme() interfaceAlgorithme()
Chaîne de responsabilités
Commande
Interpréteur
Itérateur
Médiateur
Mémento
Observateur
État
Stratégie
Patron de méthode
Visiteur
Intention
Définit, dans une opération, le squelette d’un algorithme en en
déléguant certaines étapes à des sous-classes.
Alias : —
Indications d’utilisation
I Pour implanter une fois pour toutes les parties invariantes d’un algorithme
I Pour isoler et factoriser le comportement comment à des sous-classes
I contrôler les extensions des sous-classes (garantir un certain
comportement)
Exemples :
I exécuter un test unitaire avec préparer (setUp), tester (test*) et
comptabiliser le résultat du test et nettoyer (tearDown).
ClasseAbstraite
patronMethode() ...
opérationPrimitive1() opérationPrimitive1()
opérationPrimitive2() ...
opérationPrimitive2()
...
ClasseConcrète
opérationPrimitive1()
opérationPrimitive2()
Chaîne de responsabilités
Commande
Interpréteur
Itérateur
Médiateur
Mémento
Observateur
État
Stratégie
Patron de méthode
Visiteur
Intention
Modélise par une classe une opération applicable aux éléments d’une
structure d’objets et permet de définir de nouvelles opérations sans
modifier les classes de la structure
Alias : —
Indications d’utilisation
I Une structure d’objets contient beaucoup de classes différentes
d’interfaces distinctes et l’on veut réaliser des traitements qui dépendent
de leurs classes concrètes.
I Il s’agit de réaliser plusieurs traitements distincts sans relation entre eux,
sur les objets de la structure, sans polluer leurs classes
I Les classes qui définissent la structure d’objet changent rarement mais on
doit souvent définir de nouvelles opérations sur cette struture.
Exemples : Afficher en infixe, préfixe et postfixe une expression
entière, calculer sa valeur, etc.
VisiteurConcret1 VisiteurConcret2
visiterÉlémentA1(ÉlémentA1)) visiterÉlémentA1(ÉlémentA1))
visiterÉlémentA2(ÉlémentA2) visiterÉlémentA2(ÉlémentA2)
visiterÉlémentB(ÉlémentB) visiterÉlémentB(ÉlémentB)
Client ÉlémentAbstraitA
StructureDObjets accepter(v:Visiteur)
Cercle 1
Point 2
x Segment Groupe
rayon centre
y e1, e2
PointNommé
nom
Traitements à implanter :
ObjetGéométrique
*
nom(): String
nbPoints(): int élément
translater(dx, dy)
Cercle Point
Segment Groupe
rayon x
1 y 2
nom(): String nom(): String nom(): String
centre nom(): String e1, e2 nbPoints(): int nbPoints(): int
nbPoints(): int nbPoints(): int
translater(dx, dy) translater(dx, dy) translater(dx, dy)
translater(dx, dy)
PointNommé
nom
nom(): String
nom(p: Point): String nbPoints(p: Point): int translater(p: Point, dx, dy)
nom(pn: PN): String nbPoints(pn: PN): int translater(pn: PN, dx, dy)
nom(s: Segment): String nbPoints(s: Segment): int translater(s: Segment, dx, dy)
nom(c: Cercle): String nbPoints(c: Cercle): int translater(c: Cercle, dx, dy)
nom(g: Groupe): String nbPoints(g: Groupe): int translater(g: Groupe, dx, dy)
PointNommé
nom
exécuter
Translateur
NomFrançais NombrePoints
dx, dy: double
traiter(p: Point): String traiter(p: Point): Integer traiter(p: Point)
traiter(pn: PN): String traiter(pn: PN): Integer traiter(pn: PN)
traiter(s: Segment): String traiter(s: Segment): Integer traiter(s: Segment)
traiter(c: Cercle): String traiter(c: Cercle): Integer traiter(c: Cercle)
traiter(g: Groupe): String traiter(g: Groupe): Integer traiter(g: Groupe)
exécuter(compteur)
traiter(g)
exécuter(compteur) {somme = 0}
traiter(c)
1
1 {somme = 1}
exécuter(compteur)
traiter(s)
2
2
3 {somme = 3}
Conséquences :
I il facilite l’ajout de nouveaux traitements
I un visiteur rassemble les opérations d’un même type (Visiteur concret)
I l’addition de nouvelles classes à la structure est difficile
I se promener dans une hiérarchie de classe : itérateur (partiel).
I thésaurisation des informations d’état dans un visiteur
I rupture d’encapsulation (interface des Éléments riche)
Implantation
I Double aiguillage : deux critères déterminent l’opération à effectuer, le
Visiteur et l’Élément.
I Qui organise le parcours de la structure d’objet :
F le visiteur
F la structure d’objet (souvent choisi)
F un itérateur à part