Les patrons de conception
Bibliographie
Ø « Design patterns. Catalogue des modèles de conception réutilisables », Erich
Gamma, Richard Helm, Ralph Johnson et John Vlissides
Surnommés le Gang of Four (GoF)
Privilégiez la version anglaise
ú Design Patterns: Elements of Reusable Object-Oriented Software
Ø « Java Design patterns », Vaskaran Sarcar, Apress
Ø « Head First Design Patterns », Eric Freeman, Elizabeth Freeman, O’Reilly
Ø http://sourcemaking.com/design_patterns
Ø https://refactoring.guru/design-patterns/
Introduction
Introduction
Ø Un patron de conception sert à décrire une solution standard pour des
problèmes de conception orientée objet
Un patron est donc indépendant du langage de programmation utilisé
Attention à ne pas les confondre avec les anti-patterns qui sont des exemples de
mauvaises pratiques
Ø Ils se découpent en trois grandes catégories
Création
ú Fabrication d’objets
Structure
ú Rendre les connections et les communications indépendantes de futures évolutions
Comportement
ú Structurer proprement les interactions et le comportement des objets pour supporter
de futures évolutions
Source: Head First Design Patterns
3
2
4
Introduction
Introduction
Ø Les patrons de conception respectent le plus souvent les 5 principes SOLID
Ø KISS (Keep It Simple, Stupid)
S: Single Responsability Principle
ú Une classe doit avoir une et une seule responsabilité
O: Open/Closed
ú Une classe doit être ouverte à l’extension mais fermée à la modification
L: Liskov Substitution
ú Une instance de type T doit pouvoir être remplacée par une instance de type G tant que
G est un sous type de T
I: Interface Segregation Principle
ú Préférer des interfaces spécifiques plutôt qu’une seule interface générique
D: Dependency Inversion Principle
ú Il faut dépendre des abstractions et pas des implémentations
C’est un principe général en ingénierie qui n’est pas propre à l’informatique
Un programme simple est toujours plus
simple à maintenir
Il est aussi plus simple à vérifier pour les
test et la validation des fonctionnalités
Il faut éviter l’inflation de fonctionnalités
« La simplicité est la sophistication suprême. »
Léonard de Vinci
Introduction
Ø DRY (Don’t Repeat Yourself)
Le copier coller de code est mal !
Si vous êtes amené à dupliquer du code, c’est que vous avez besoin de
refactoriser ou de reprendre la conception
Il faut utiliser les patrons de conception
L’héritage et la composition peuvent l’éviter
Ø Le concept peut se résumer à « Ecrivez le code une fois, réutilisez le souvent »
5
7
Introduction
Ø YAGNI (You Aren’t Gonna Need It)
Toute fonctionnalité doit être documentée, testée et débuggée
ú Si une fonctionnalité n’est pas demandée elle n’est probablement
pas nécessaire
ú Si elle n’est pas spécifiée, les tests et la documentation ne pourront
pas être définis complètement
ú Ajouter des fonctionnalités inutiles complexifie le logiciel
« On parle de dette technique
ú En ajoutant une première fonctionnalité inutile on est souvent tenté
d’en ajouter d’autres -> Effet boule de neige
Source: xkcd
6
8
Les patrons de création
Ø C’est le patron de conception le plus simple et le plus connu mais il en existe
Le patron Singleton
Ø Le patron Singleton permet de répondre à deux exigences
Assurer qu’une instance unique d’une classe sera créée
Offrir un point d’accès unique et universel à cette instance
cependant plusieurs versions
Le « Lazy »
Le « synchronized »
Le « eager »
Le « Holder »
10
Le patron Singleton
Le patron Singleton
Ø Lazy Singleton
public class LazySingleton {
private static LazySingleton instance = null;
Ø Synchronized Singleton
public class SyncSingleton {
private static SyncSingleton instance = null;
// Constructeur privé
private LazySingleton(){
}
// Méthode statique pour obtenir l’instance
public static LazySingleton getInstance(){
if (instance == null)
instance = new LazySingleton();
return instance;
}
}
// Constructeur privé
private SyncSingleton(){
}
// Méthode statique pour obtenir l’instance
public static synchronized LazySingleton getInstance(){
if (instance == null)
instance = new LazySingleton();
return instance;
}
}
Ø Cette solutionne fonctionne parfaitement tant qu’il n’y a pas d’accès
Ø Ce patron répond au problème précédent avec une pénalité importante sur les
concurrents
performances
Si deux threads accèdent simultanément à la méthode getInstance() on peut
Ø Passage par une file d’attente de processus qui n’est pas nécessaire si les accès concurrents
construire deux fois l’objet
sont réduits
11
12
Le patron Singleton
Le patron Singleton
Publicité
Ø Eager Initialization
public class EagerSingleton {
private static EagerSingleton instance = new EagerSingleton();
Ø Le « Holder »
public class HSingleton {
// Constructeur privé
private EagerSingleton(){
}
// Méthode statique pour obtenir l’instance
public static EagerSingleton getInstance(){
return instance;
}
}
Ø Avec cette méthode, on assure que le singleton ne sera bien construit qu’une
seule fois même dans un environnement multi-threads
Une solution similaire consiste à utiliser un bloc static {…} pour réaliser
l’initialisation
// Constructeur privé
private HSingleton(){
}
private static class SingletonHolder{
private static final HSingleton instance = new HSingleton();
}
// Méthode statique pour obtenir l’instance
public static getInstance(){
return SingletonHolder.instance;
}
}
Ø Proposé par Bill Pugh (Université du Maryland) pour le langage Java
Solution adoptée par la plupart des frameworks utilisant le singleton
Reconnu comme le plus simple et le plus performant même en multi-thread
13
14
Le patron Factory (Fabrique)
Le patron Factory (Fabrique)
Ø Définition du GoF: "Define an interface for creating an object, but let
La version à une seule fabrique
subclasses decide which class to instantiate. The Factory method lets a class
defer instantiation it uses to subclasses.“
Pour résumer, une fabrique consiste à isoler la création des objets de leur
utilisation
ú En effet le type va souvent dépendre du contexte d’utilisation
ú Le client ne va pouvoir déterminer le type qu’à l’exécution
Elle vise également à centraliser la création des objets en fonction du contexte
Ø En général, il existe deux versions de ce patron
Une version avec une seule Fabrique qui doit retrouver le type d’objet souhaité en
fonction du contexte
Une version avec une Fabrique abstraite de laquelle hérite des fabriques
permettant d’obtenir les instances souhaitées
Source: http://www.jmdoudoux.fr/java/dej/chap-design-patterns.htm
15
16
Le patron Factory (Fabrique)
Le patron Abstract Factory
Ø Prenons un exemple simple: La pizza
Il peut y avoir plusieurs types de pizzas:
ú ReginaPizza
ú MargheritaPizza
ú …
Pizza
ReginaPizza
MargheritaPizza
Ø Si nous reprenons la version avec une Fabrique, une solution est
public class PizzaFactory{
public Pizza getPizza(String nom){
if (nom==REGINA) return new ReginaPizza();
else return new MargheritaPizza();
}
}
Ø Pbm: Si on veut ajouter un nouveau type de pizza à la carte, il faut modifier la
méthode getPizza() ce qui viole la règle O de SOLID
17
Ø Le patron Abstract Factory permet de répondre à ce problème
On procède par héritage d’une classe abstraite pour les extensions plutôt qu’en
modifiant le contenu des méthodes
public abstract class PizzaFactory{
public abstract Pizza getPizza();
}
On définit ensuite une Fabrique concrète par type de Pizza à retourner
public class ReginaFactory extends PizzaFactory{
public class MargheritaFactory extends PizzaFactory{
public Pizza getPizza(){
return new ReginaPizza();
public Pizza getPizza(){
return new MargheritaPizza();
}
}
}
}
Pour ajouter un nouveau type, il suffit d’écrire une nouvelle classe héritant de la
Fabrique abstraite
Le patron Abstract Factory
Le patron Abstract Factory
La version à plusieurs fabriques
Ø Pour le langage Java une version alternative de ce Pattern existe qui repose
sur l’utilisation d’une interface pour définir l’Abstract Factory
Permet de gérer les problèmes d’héritages multiples
public interface PizzaFactory{
public Pizza getPizza();
}
public class ReginaFactory implements PizzaFactory{
public Pizza getPizza(){
return new ReginaPizza();
}
}
public class MargheritaFactory implements PizzaFactory{
public Pizza getPizza(){
return new MargheritaPizza();
}
}
Source: http://www.jmdoudoux.fr/java/dej/chap-design-patterns.htm
19
18
20
Le patron Abstract Factory
Ø Il permet d’avoir du code particulièrement modulaire comme dans le
diagramme UML suivant
Les patrons de structure
Source: Head First
Design Patterns
Le patron DAO
Ø DAO: Data Access Object
Ø Motivation
21
Le patron DAO
Ø Diagramme de classe générale du patron de conception
La persistance des données est très souvent nécessaire dans une application mais
elle peut prendre plusieurs formes
ú Fichiers
Publicité
ú Bases de données locales ou réparties
ú Services web
ú …
Cette persistance peut évoluer au fil du temps
ú Il faut donc éviter de la disperser dans le code pour en faciliter la maintenabilité
Ø Principe: Isoler la couche de persistance du reste de l’application
Il complète le modèle MVC classique
Ø Diagramme de séquence associé
23
24
Le patron Adaptateur
Le patron Adaptateur
Ø Définition du GoF : “Convert the interface of a class into another interface
that clients expect. The adapter pattern lets classes work together that
couldn’t otherwise because of incompatible interfaces.”
L’idée est de proposer des classes qui servent d’adaptateur à des classes
existantes pour leur permettre de fonctionner avec d’autres classes
Ø Le code à écrire est donc limité à l’adaptateur pour faire un lien entre les
classes à relier
Source: Head First
design Patterns
Le patron Adaptateur
Ø De manière plus formelle en UML
Source: Head First Design Patterns
25
27
Le patron Adaptateur
Ø Prenons un exemple: Une interface Chien
public interface Chien{
public void aboyer();
public void mordre();
}
Ø Cette interface peut être implémenter par plusieurs classes comme par
exemple
public class Bouledogue implements Chien{
public void aboyer(){
System.out.println("ouaf, ouaf");
}
public void mordre(){
System.out.println("je mord!");
}
}
26
28
Le patron Adaptateur
Le patron Adaptateur
Supposons que nous tombions à court de Bouledogues mais que par un heureux
Avec la classe:
public void feuler(){
public class FuretSauvage implements Furet{
hasard nous ayons un remplaçant sous la main…
Définit par l’interface:
public interface Furet{
public void feuler();
public void mordre();
}
29
System.out.println("poutpout"); // si si vraiment…
}
public void mordre(){
System.out.println("Je mordille");
}
}
L’adaptateur est
public class FuretAdaptateur implements Chien{
private Furet monFuret;
public FuretAdaptateur(Furet furet){
this.monFuret = furet;
}
public void aboyer(){
monFuret.feuler();
}
public void mordre(){
monFuret.mordre();
}
}
Le patron Adaptateur
Le patron Décorateur
Ø Ce patron est très pratique pour permettre de relier deux parties de code ne
Ø Le patron décorateur est assez proche du précédent
présentant pas de types communs
Faites tout de même attention en l’utilisant car permettre une adaptation entre
deux objets très dissemblables risque de vous poser des problèmes pour fournir
les fonctionnalités attendues…
Il ajoute des fonctionnalités à l’interface existante contrairement à l’adaptateur
qui lui essayait d’adapter l’interface
Source:
http://www.dofactory.com/net/design-
patterns
31
30
32
Le patron Décorateur
Ø Si nous reprenons l’exemple de la pizza comme pouvons nous ajouter des
ingrédients à volonté ?
En créant de nouveaux types qui héritent de Pizza
ú ReginaPizzaAuxAnchoix
ú ReginaPizzaAuxCapres
ú ReginaPizzaAuxOignons
ú …
Cette solution n’est pas satisfaisante car rapidement on voit se profiler une
explosion du nombre de classes et de types
Ø L’objectif de ce patron est d’éviter cette explosion de classes en empaquetant
un objet à l’intérieur d’un autre pour lui ajouter des fonctionnalités
Le patron Décorateur
Ø Ce qui donne en Java
public abstract class Pizza {
public abstract Pizza preparer();
}
public class ReginaPizza extends Pizza{
public String getIngredients(){ return "Jambon, Fromage, Champignons"; }
}
public abstract class PizzaDecorateur extends Pizza {
protected Pizza baseDePizza;
}
public class PizzaAuxOlives extends PizzaDecorateur {
public PizzaAuxOlives(Pizza base) {
this.baseDePizza = base;
}
public String getIngredients(){ return baseDePizza.getIngredients()+ ", Olives"); }
}
33
35
Le patron Décorateur
Pizza
+ getIngredients(): String
baseDePizza
ReginaPizza
MargheritaPizza
Publicité
PizzaDecorateur
+ getIngredients(): String
+ getIngredients(): String
PizzaAuxCapres
PizzaAuxOlives
- Pizza maPizza
+ getIngredients(): String
- Pizza maPizza
+ getIngredients(): String
34
Le patron Façade
Ø Le patron Façade permet de mettre en place simplement une interface pour
accéder à une librairie, un framework ou un ensemble complexe
Il permet de masquer cette complexité en exposant un sous ensemble de
fonctionnalités
Il offre souvent moins de fonctionnalités que le système masqué
ú Permet cependant de faire évoluer le système sans que la façade soit affectée
ú Avantage lorsqu’on n’a besoin que de quelques fonctionnalités dans une grosse librairie
Source: Wikipédia
36
Le patron Façade
Ø Exemple de Façade: Un outil de conversion vidéo
L’outil va devoir manipuler des composants manipulant l’audio, la vidéo, les
codecs…
Les patrons de comportement
37
Le patron de méthode (Template method)
Le patron de méthode (Template method)
Ø Il permet de faire partager un comportement par plusieurs classes
Ø Ce patron permet de s’assurer que d’étendre une partie du code sans toucher
La méthode TemplateMethod définit un comportement général qui s’appuie sur
à l’algorithme général
deux méthodes abstraites: PrimitiveOperation1() et
PrimitiveOperation2()
Les deux méthodes sont ensuite surchargées dans les classes héritantes
Permet d’éclater un code monolithique en plusieurs étapes qui peuvent ensuite
être surchargées dans des sous-classes sans changer le comportement global
Source: Head First Design Patterns
39
Source: refactoring.guru
40
Le patron Observable (Observer)
Le patron Observable (Observer)
Ø L’objectif de ce patron est de permettre de signaler à un observateur les
Ø L’objet observé est appelé le plus souvent sujet
changements qui surviennent sur un objet
En Java, nous l’avons déjà manipulé à travers le PropertyChangeSupport
Il revient à définir un mécanisme de souscription pour informer les observateurs
des changements qui surviennent sur l’objet observé
On peut également le qualifier de publieur dans la mesure où il va publier des
informations aux observateurs
Cet objet propose une méthode permettant à un observateur de s’enregistrer
pour obtenir des notifications sur les changements d’états
Ø Les observateurs de leur coté doivent implémenter une même interface dans
laquelle la méthode de notification est définie
Cela permet d’homogénéiser la manière dont le sujet va répondre
Source: refactoring.guru
41
Le patron Observable (Observer)
Le patron Observable (Observer)
Ø Diagramme UML générique
Ø Exemple (source: sourcemaking)
Source: refactoring.guru
43
42
44
Le patron Observable (Observer)
Le patron d’états (State pattern)
Ø Le but est de permettre à un objet de changer son comportement lorsque son
état interne change
Le changement semble produire un changement de classe vu de l’extérieur de
l’objet
Ø Diagramme UML
L’objet Context est
composé d’un état
L’état est réalisé par
deux classes différentes
En général des
singletons
45
Source: Head First Design Patterns
Le patron d’états (State pattern)
Le patron d’états (State pattern)
Ø Prenons un exemple simple: Un distributeur automatique
Ø Exemple (source: sourcemaking)
Il propose plusieurs
comportements lorsque l’on
insère de la monnaie et que
l’on sélectionne un produit
ú Il distribue le produit
ú Il distribue le produit puis
rend la monnaie
ú Il ne distribue pas le
produit car le montant
fourni est trop faible
ú Il ne distribue pas le
produit car il n’a plus de
produits demandés
Source: sourcemaking
47
46
48
Le patron d’états (State pattern)
Le patron médiateur
Ø Le rôle du patron médiateur est d’encapsuler la manière dont des objets vont
interagir
Il évite les communications directes entre objets
Assure un couplage faible entre les objets à mettre en relation car ils n’ont pas
besoin de se référencer pour interagir
ú Il met en général en place un mécanisme d’évènement pour permettre aux objets de
dialoguer
Source: refactoring.guru
49
50
Le patron médiateur
Le patron médiateur
Ø Exemple de situation problématique
Ø Ce patron est utile lorsque les classes sont fortement liées les unes aux autres
Il permet de spécifier ces relations au sein de la classe de médiateur
Ø Solution avec le patron médiateur
Source:
refactoring.guru
Ø Il évite de créer de multiples sous classes juste pour s’adapter à un nouveau
contexte d’exécution
En cas de nouveau contexte on spécifie un nouveau médiateur qui exprime le
nouveau mode de collaboration des classes entre elles
Ø Attention cependant dans l’utilisation de la classe médiateur qui ne doit pas
devenir un « God Object »
Un objet dieu est un objet qui connait trop de choses et fait trop de choses
Publicité
Source:
refactoring.guru
51
52
Le patron médiateur
Le patron médiateur
Ø Diagramme de classe par rapport à l’exemple
Ø De manière plus générique le patron se représente de la manière suivante
Le médiateur est la classe AuthenticationDialog
Les éléments ne communiquent plus directement entre eux
ú Ils notifient juste le médiateur d’un nouvel évènement
Source:
refactoring.guru
53
Les composants possèdent
une référence vers
l’interface médiateur
ú Permet de les réutiliser
dans d’autre contexte avec
d’autres médiateurs
Les composants n’ont pas
connaissance les uns des
autres
Le médiateur déclare une
méthode de communication
avec les composants
Source:
refactoring.guru
54
Le patron médiateur
Le patron médiateur
Ø Ce patron présente de forte similarité avec le patron observateur
Ø Exemple
Ils sont souvent interchangeables
Ø Ils peuvent cependant être utilisés en même temps
Le but du médiateur est d’assurer un couplage faible entre les composants
Le but de l’observateur est de fournir une méthode pour notifier des
changements
Une implémentation du médiateur intègre en fait observateur dans le sens où
l’objet médiateur joue le rôle du sujet
Ø La principale différence repose sur le fait que le médiateur peut avoir des
références permanentes vers les composants
L’observateur repose sur une notion d’abonnement/désabonnement
55
56
Le patron médiateur
Le patron médiateur
57
58
Le patron Visiteur
Le patron Visiteur
Ø Le patron de conception Visiteur permet de séparer les algorithmes des
objets qui vont les utiliser
Utilisé pour ajouter des fonctionnalités à un code existant sans l’altérer
Ø La classe Visiteur va recevoir en paramètre de ses méthodes, les composants
auxquels les fonctionnalités doivent être ajoutées
Les composants vont de leur coté appeler la méthode de visite sur chaque visiteur
au travers d’une méthode accept(…) qui autorise les visites
Ø Ce patron est utile lorsque l’on a des opérations à effectuer sur des objets
complexes
Permet de limite le code de ces objets à la logique métier et d’externaliser le reste
59
Ø L’interface Visiteur déclare les
méthodes de visite pour les
composants
Les Visiteurs implémentent
cette interface pour les objets
concrets
Ø L’interface Element déclare une
méthode pour accepter les
visiteurs
Ø Les composants concret
implémentent la méthode pour
accepter les visiteurs
Dans la méthode accept ils
font appel à visit(…)
Source:
refactoring.guru
60
Le patron Visiteur
Le patron Visiteur
Ø Visiteur permet de n’affecter qu’une partie des composants au sein d’une
Ø Exemple (source:sourcemaking)
hiérarchie de composants
On met le code concerné dans une classe visiteur et seuls les composants ayant
besoin de ce comportements vont accepter ce visiteur
Ø Il respecte les principes Open/Close et Single Responsability de SOLID
Un nouveau comportement est ajouté sous la forme d’un nouveau Visiteur
Ø Le visiteur peut également servir à récupérer de l’information lorsqu’il visite
des systèmes complexes
Ø Par contre, il est nécessaire de mettre à jour les visiteurs à chaque fois qu’un
composant est ajouté ou supprimé de la hiérarchie et il en peut pas accéder
au champs/méthodes privées
Le patron Visiteur
Le patron Visiteur
61
63
62
64
Le patron Stratégie
Le patron Stratégie
Ø Le patron stratégie est un des patrons de comportement les plus importants
Ø Le patron Stratégie respecte le S de SOLID
Parmi les plus utilisés à l’heure actuelle dans de nombreux framework
En effet, lorsque le nombre de fonctionnalités augmente, il vaut mieux les séparer
Ø Il permet de sélectionner au moment l’exécution l’algorithme à utiliser
Au lieu d’implémenter à l’avance un algorithme, le code reçoit lors de l’exécution
les instructions nécessaire pour faire le choix parmi une famille d’algorithme
Le patron Stratégie sépare ces fonctionnalités au sein d’un même objet
ú Par exemple un objet de validation va pouvoir changer d’algorithme en fonction du type
à tester qu’on lui fourni
Ø Il respecte également le principe Open/Close
Le patron déconseille l’héritage et recommande l’utilisation d’interfaces
ú Les classes sont ainsi ouvertes à l’extension à travers les interfaces et fermées à la
modification en n’autorisant pas l’héritage
Utilisation d’un attribut représentant la stratégie qui sera de type abstrait
ú En changeant le type concret, on change le comportement de l’objet
L’utilisation des interfaces permet aussi de changer dynamiquement le
comportement en cours d’exécution (à travers un setter sur la stratégie)
Source: Sourcemaking
65
66
Le patron Stratégie
Ø Exemple: Pierre-Feuille-Ciseau
Source: Jean-Christophe Routier
67