Introduction aux Patrons de Conception
Introduction aux Patrons de Conception
(Design Patterns)
Nan MESSE
[email protected]
Rappel des concepts fondamentaux de l’Orienté Objet
- Classe
- Objet
2
Abstraction
3
Encapsulation
- L’encapsulation est la technique qu’un objet utilise pour cacher une partie de son état et de ses
comportements aux autres objets, pour ne révéler qu’une interface limitée au reste du programme.
- private ou protected
4
Héritage
5
Polymorphisme
- Le polymorphisme est la capacité d’un programme à détecter la classe d’un objet et à appeler son
implémentation, même si son type est inconnu dans le contexte actuel.
6
Héritage et Composition
7
Couplage fort
8
Couplage faible
9
Pourquoi l’approche Orientée Objet a été créée ?
10
● Réutilisation du code
d’une bonne -
concrètes
les codes écrit en dur…
● Extensibilité
11
● Encapsuler ce qui varie et le séparer
de ce qui est statique
Principes de +
modification
au niveau méthode et classe
● Programmer avec les interfaces, et
conception non pas avec les implémentations
12
● Encapsuler ce qui varie et le séparer
de ce qui est statique
13
● Encapsuler ce qui varie et le séparer
de ce qui est statique
14
● Encapsuler ce qui varie et le séparer
de ce qui est statique
15
● Encapsuler ce qui varie et le séparer
de ce qui est statique
Extensibilité Flexibilité
16
La classe Société est toujours couplée aux classes des employés. Nous allons nous retrouver en mauvaise posture si nous devons
ajouter de nouveaux types de sociétés qui hébergent d’autres types d’employés, car nous allons devoir redéfinir la majeure partie de la
classe Société, plutôt que de réutiliser pour son code.
17
Fabrique
Maintenant, la classe
Société est devenue
indépendante des diverses
classes des employés.
Vous pouvez désormais l’
étendre et introduire de
nouveaux types de
sociétés et d’employés,
tout en réutilisant une
partie du code de la classe
de base société. Étendre la
classe de base société ne
modifie pas le code
existant qui en dépend.
18
● Préférer la composition à l’héritage
conception -
classe mère
Les sous-classes sont fortement couplées
aux classes mères : la moindre
modification de la classe mère peut
impacter le fonctionnement de ses
sous-classes
- Peut avoir une explosion combinatoire de
sous-classes
19
Stratégie
● introduit par Christopher Alexander dans “A Pattern Language: Towns, Buildings, Construction”
● 1977
● repris par Erich Gamma, John Vlissides, Ralph Johnson, et Richard Helm
● dans “Design Patterns: Elements of Reusable Object-Oriented Software”
● 1994, 23 patrons
● remplacé par “the book by the Gang of Four”, puis “the GoF book”
Collections
● Pattern-Oriented Software Architecture, Volume 1: A System of Patterns des
● 1996 Patrons
● Frank Buschmann, Regine Meunier, Hans Rohnert, Peter Sommerlad and
Michael Stal
● POSA1 book
Ce sont des solutions classiques à des problèmes connus en conception orientée objet.
Lorsqu’une question revient encore et encore dans différents projets, quelqu’un se
décide finalement à détailler la solution et à lui donner un nom. C’est souvent comme 21
cela qu’un patron est découvert.
Patron de Conception (Design Pattern) - Définition
Les patrons de conception sont des solutions classiques à des problèmes récurrents de la
conception de logiciels [1].
Un patron n’est pas un bout de code spécifique, mais plutôt un concept général pour résoudre un
problème précis.
Chaque patron est une sorte de plan ou de schéma que vous pouvez personnaliser afin de
résoudre un problème récurrent dans votre code.
22
Pourquoi apprendre les patrons ?
● Une boîte à outils de solutions fiables et éprouvées permettant de
résoudre des problèmes classiques de la conception de logiciels.
● Une identification et spécification d’abstractions qui sont au-dessus du
niveau des simples classes et instances (s’applique aux différents
scénarios).
● Un vocabulaire commun de se communiquer -> “Oh, tu n’as qu’à utiliser un
singleton”.
● Un moyen de documentation de logiciels.
● Une aide à la construction de logiciels complexes et hétérogènes,
répondant à des propriétés précises. 23
Patrons vs. Algorithmes ?
Patron Algorithme
décrit une solution à un plus haut niveau définit toujours clairement un ensemble
d’actions qui va vous mener vers un objectif
Le code utilisé pour implémenter un même précis
patron peut être complètement différent s’il
est appliqué à deux programmes distincts
Un plan, reproductible dans tous les Une recette de cuisine, ses étapes sont
contextes claires
24
Classification des Design Patterns
- Portée de Classe
- Focalisation sur les relations entre classes et leurs sous-classes
- Réutilisation par héritage
26
23 Patrons GoF
27
https://slideplayer.com/slide/6667469/
Patrons POSA1
Layers Whole-Part
Blackboard Proxy
Presentation-Abstraction-Control Forwarded-Receiver
Microkernel Client-Dispatcher-Server
28
Reflection Publisher-Subscriber
Présentation d’un Design Pattern
● Nom du patron
○ utilisé pour décrire le patron, ses solutions et les conséquences en un mot ou deux
● Problème
○ description des conditions d’applications. Explication du problème et de son contexte
● Solution
○ description des éléments (objets, relations, responsabilités, collaboration)
○ permettant de concevoir la solution au problème ; utilisation des diag. de classes, de
séquences, …
○ vision statique ET dynamique de la solution
● Conséquences
○ description des résultats (effets induits) de l’application du patron sur le système (positifs
ET négatifs)
29
● Fabrique (Factory Method)
GoF
● Fabrique abstraite (Abstract
Factory)
Patrons de création
● Monteur (Builder)
fournissent des mécanismes de
création d’objets (ce qui augmente la ● Prototype
flexibilité et la réutilisation du code)
● Singleton
30
Singleton
Singleton garantit la création d’une instance unique d’une classe durant toute la durée
d’exécution d’une application, tout en fournissant un point d’accès global à cette
instance.
Objectifs : Il garantit l’unicité d’une instance pour une classe et il fournit un point
d’accès global (une méthode) à cette instance.
Le singleton vous permet d’accéder à l’objet n’importe où dans le programme, telle une
variable globale. Cependant, il protège son instance et l’empêche d’être modifiée.
On l’utilise lorsqu’on veut contrôler l’accès à une ressource partagée, par ex. 31
Singleton - Solution, Analogie et Structure
- Rendre le constructeur par défaut privé afin d’empêcher les autres objets d’utiliser
l’opérateur new avec la classe du singleton
- Mettre en place une méthode de création statique qui se comporte comme un
constructeur. Cette méthode appelle le constructeur privé pour créer un objet et le
sauvegarde dans un attribut statique. Tous les appels ultérieurs à cette méthode
retournent l’objet en cache.
- Exemple : gouvernement
La classe de la
connexion à la base de
données est le Singleton.
Cette classe n’a pas de
constructeur public, vous
ne pouvez y accéder que
grâce à la méthode
getInstance.
Cette méthode met en
cache le premier objet
créé puis retourne ce
même objet lors des
appels ultérieurs.
33
Singleton - Pseudo-code avec un exemple bdd
34
Singleton - Possibilités d’application
- Utilisez le singleton lorsque l’une de vos classes ne doit fournir qu’une seule instance à tous
ses clients.
- Ex. une base de données partagée entre toutes les parties d’un programme.
- La méthode spéciale de création devient le seul moyen de fabriquer des objets pour la
classe, car le singleton désactive les autres. Cette méthode crée un objet ou retourne
l’objet existant s’il a déjà été créé.
- Utilisez le singleton lorsque vous voulez un contrôle absolu sur vos variables globales.
- Exercice : https://www.wooclap.com/L3502
35
● Adaptateur (Adapter)
● Pont (Bridge)
GoF
● Composite
Patrons Structurels ● Décorateur (Decorator)
expliquent comment assembler des ● Façade (Facade)
objets et des classes en de plus
grandes structures ● Poids mouche (Flyweight)
● Procuration (Proxy)
36
Composite
37
Composite - Calculer le coût total d’une commande
Pour un produit, on retourne simplement son prix. Pour une boîte, on parcourt chacun
de ses objets, on leur demande leur prix, puis on retourne un total pour la boîte. Si l’un
de ces objets est une boîte plus petite, cette dernière va aussi parcourir son propre
contenu et ainsi de suite, jusqu’à ce que tous les prix aient été calculés.
Vous n’avez même pas besoin de connaître la classe concrète des objets de
l’arborescence.
Vous n’avez pas besoin de savoir si un objet est un produit tout simple ou une boîte
sophistiquée, vous les manipulez de la même manière grâce à une interface commune.
Lorsque vous faites appel à une méthode, les objets s’occupent de faire transiter la
requête en descendant vers les feuilles de l’arbre. 38
Composite - Structure
39
Composite - Exemple de l’éditeur des formes géométriques
40
41
42
43
Composite - Possibilités d’application
- Utilisez le composite si vous devez gérer une structure d’objets qui ressemble à une
arborescence.
- Utilisez ce patron si vous voulez que le client interagisse avec les éléments simples aussi
bien que complexes de façon uniforme.
- Exercice : https://www.wooclap.com/EPIRIZ
44
● Chaîne de Responsabilité (Chain of Responsability)
● Commande (Command)
● Médiateur (Mediator)
Patrons ● Mémento (Memento)
Comportementaux ● Observateur (Observer)
● Visiteur (Visitor)
45
Observer
L’Observateur permet de mettre en place un mécanisme de souscription pour
envoyer des notifications à plusieurs objets, au sujet d’événements concernant
les objets qu’ils observent.
Soit les clients perdent leur temps à venir vérifier la disponibilité des produits, soit le magasin gâche des ressources pour prévenir des clients qui ne sont pas concernés.
46
Observer
- L’objet que l’on veut suivre est en général appelé sujet, mais comme il va
envoyer des notifications pour prévenir les autres objets dès qu’il est
modifié, nous l’appellerons diffuseur (publisher). Tous les objets qui
veulent suivre les modifications apportées au diffuseur sont appelés des
souscripteurs (subscribers) -> relation un à plusieurs.
- Le patron Observateur vous propose d’ajouter un mécanisme de
souscription à la classe diffuseur pour permettre aux objets individuels de
s’inscrire ou se désinscrire de ce diffuseur.
- À chaque fois que l’état de diffuseur change, tout ce qui en dépendent en
soient informés et soient mis à jour automatiquement.
47
Push and Pop : 2 moyens d’observer
48
Observer
49
Observer - Structure
1. Le Diffuseur envoie des événements intéressants à d’autres objets. Ces événements se produisent quand
le diffuseur change d’état ou exécute certains comportements. Le diffuseur possède une infrastructure
d’inscription qui permet aux nouveaux souscripteurs de rejoindre la liste et aux souscripteurs actuels de la
quitter.
50
Observer - Structure
2. Quand un nouvel événement survient, le diffuseur parcourt la liste d’inscriptions et appelle la méthode de
notification déclarée dans l’interface des souscripteurs sur chaque objet souscripteur.
3. L’interface Souscripteur déclare la méthode de notification update(). Elle peut prendre plusieurs paramètres
pour que le diffuseur leur envoie plus de détails concernant la modification.
51
Observer - Structure
4. Les Souscripteurs Concrets exécutent certaines actions en réponse aux notifications envoyées par le
diffuseur. Toutes ces classes doivent implémenter la même interface pour ne pas coupler le diffuseur avec leurs
classes concrètes.
52
Observer - Structure
5. En général, les souscripteurs ont besoin de détails à propos du contexte afin d’exécuter correctement la mise à
jour. C’est pour cela que les diffuseurs passent souvent des données du contexte en paramètre de la méthode de
notification.
6. Le Client crée des objets diffuseur et Souscripteur séparément et inscrit les souscripteurs aux mises à jour du
diffuseur. 53
Observer - Exemple avec l’éditeur de texte
54
Observer - Exemple avec l’éditeur de texte
55
Observer - Exemple avec l’éditeur de texte
56
Observer - Possibilités d’application
- Utilisez l'Observateur quand des modifications de l’état d’un objet peuvent en impacter
d’autres, et que l’ensemble des objets n’est pas connu à l’avance ou qu’il change
dynamiquement.
- Utilisez ce patron quand certains objets de votre application doivent en suivre d’autres,
mais seulement pendant un certain temps ou dans des cas spécifiques.
- Exercice : https://www.wooclap.com/EPIRIZ
57
Strategy
58
Strategy
Raison d’utilisation : un objet doit pouvoir faire varier une partie de son algorithme dynamiquement.
Résultat : le patron stratégie permet d’isoler les algorithmes appartenant à une même famille d’algorithmes,
mais implique de créer beaucoup d’objets en mémoire 59
Strategy - Structure
60
Strategy - Exemple avec les opérations arithmétiques
61
Strategy - Exemple avec les opérations arithmétiques
62
Strategy - Possibilités d’application
- Utilisez le Stratégie si vous voulez avoir différentes variantes d’un algorithme à l’intérieur
d’un objet à disposition, et pouvoir passer d’un algorithme à l’autre lors de l’exécution.
- Utilisez la stratégie pour isoler la logique métier d’une classe, de l’implémentation des
algorithmes dont les détails ne sont pas forcément importants pour le contexte.
- Utilisez ce patron si votre classe possède un gros bloc conditionnel qui choisit entre
différentes variantes du même algorithme.
- Exercice : https://www.wooclap.com/EPIRIZ
63
Visitor
Visiteur vous permet de séparer les algorithmes et les objets sur lesquels ils
opèrent.
64
Visitor
Comment fait-on pour que ce comportement puisse être exécuté sur des objets de différentes classes ?
65
Visitor
La classe visiteur va donc avoir besoin d’un ensemble de méthodes et chacune d’entre elles pourra prendre
des paramètres de différents types, comme ce qui suit :
Mais comment allons-nous appeler ces méthodes, surtout celles qui gèrent le graphe complet ?
66
Visitor
Le patron Visiteur utilise une technique appelée double répartition (double dispatch), qui aide à lancer la
bonne méthode sans s’encombrer avec des blocs conditionnels.
67
Visitor - Structure
1. L’interface Visiteur déclare un ensemble de méthodes de
parcours qui peuvent prendre les éléments concrets d’une
structure d’objets en paramètre. Ces méthodes peuvent
avoir le même nom si le programme est écrit dans un
langage qui gère la surcharge, mais le type de ses
paramètres sera différent.
2. Chaque Visiteur Concret implémente plusieurs versions
des mêmes comportements, en fonction des classes des
éléments concrets.
3. L’interface Élément déclare une méthode qui « accepte » les
visiteurs. Cette méthode déclare un paramètre du type de
l’interface visiteur.
4. Chaque Élément Concret doit implémenter une méthode
d’acceptation. Le but de cette méthode est de rediriger
l’appel vers la méthode appropriée du visiteur en fonction
de la classe de l’élément actuel.
5. Le Client représente en général une collection ou tout autre
objet complexe (par exemple un arbre Composite). En
général, les clients n’ont pas de visibilité sur les classes des
éléments concrets, car ils manipulent les objets de cette68
collection via une interface abstraite.
Visitor - Exemple avec l’export XML
69
Visitor - Exemple avec l’export XML
70
Visitor - Exemple avec l’export XML
71
Visitor - Exemple avec l’export XML
72
Visitor - Possibilités d’application
- Utilisez le visiteur lorsque vous voulez lancer des traitements sur les éléments d’un objet
ayant une structure complexe (une arborescence par exemple).
- Visiteur vous permet de lancer des traitements sur un ensemble d’objets de différentes
classes à l’aide d’un objet visiteur qui implémente une variante d’un même traitement pour
chaque classe visée.
- Utilisez le visiteur si un comportement n’est adapté que pour certaines classes d’une
hiérarchie de classes, mais pas pour les autres.
- Exercice : https://www.wooclap.com/EPIRIZ
73
23 Patrons GoF
74
https://slideplayer.com/slide/6667469/
Comparaison
- Vous pouvez utiliser le Visiteur pour lancer une opération sur un arbre
Composite entier.
75
Patrons POSA1
Layers Whole-Part
Blackboard Proxy
Presentation-Abstraction-Control Forwarded-Receiver
Microkernel Client-Dispatcher-Server
76
Reflection Publisher-Subscriber
● Layers
POSA1 ● Blackboard
● Broker
Patrons d’architecture
● Model-View-Controller
exprime un schéma d’organisation
structurel fondamental pour un système ● Presentation-Abstraction-Control
logiciel
● Microkernel
● Reflection
77
Model-View-Controller (MVC)
79
Model-View-Controller (MVC)
80
Model-View-Controller (MVC)
82
1. Single Responsibility Principle
2. Open/Closed Principle
Principes SOLID 3. Liskov Substitution Principle
4. Interface Segregation Principle
5. Dependency Inversion Principle
83
Single Responsibility Principle
Une classe ne devrait être modifiée que pour une seule raison.
Essayez de faire en sorte qu’une classe ne soit responsable que d’une partie
d’une fonctionnalité de votre logiciel, et encapsuler (vous pouvez aussi dire
cachez à l’intérieur) cette responsabilité entièrement dans la classe.
Si vous éprouvez des difficultés à vous concentrer sur des aspects spécifiques
du programme, souvenez-vous de ce principe et vérifiez s’il est grand temps de
découper certaines classes en plusieurs parties.
84
Single Responsibility Principle - Example
85
Open/Closed Principle
L’idée est d’éviter de créer des bugs dans du code existant lorsque vous ajoutez de
nouvelles fonctionnalités.
Une classe est ouverte si vous pouvez l’étendre, créer une sous-classe ou faire
n’importe quoi d’autre avec ; Une classe est fermée si elle est prête à 100 % à être
utilisée par d’autres classes (son interface est clairement définie et ne sera plus
modifiée dans le futur).
86
Open/Closed Principle - Example
87
Open/Closed Principle - Example
Stratégie
88
Liskov Substitution Principle
Lorsque vous étendez une classe, rappelez-vous que vous devez être en mesure de
passer des objets de la sous-classe à la place des objets de la classe mère sans
faire planter le code.
89
Liskov Substitution Principle : prérequis des sous-classes
90
Liskov Substitution Principle : prérequis des sous-classes
La méthode de la sous-classe
DocumentEnLectureSeule lève une exception
si elle est appelée. La méthode de base ne
possède pas cette restriction.
92
Liskov Substitution Principle - Exemple
93
Interface Segregation Principle
Les clients ne devraient pas être forcés à dépendre de méthodes qu’ils n’utilisent pas.
Essayez de rendre vos interfaces aussi étroites que possible afin de ne pas obliger les
classes des clients à implémenter des comportements dont elles n’ont pas besoin ->
découper vos « grosses » interfaces pour les rendre plus précises et spécifiques.
Les clients ne doivent implémenter que les méthodes dont ils ont vraiment besoin.
Sinon, une modification apportée dans une « grosse » interface va même faire planter
les clients qui n’utilisent pas les méthodes modifiées.
94
Interface Segregation Principle - Exemple
Les classes de haut niveau ne devraient pas dépendre des classes de bas niveau. Elles
devraient dépendre toutes les deux d’abstractions. Une abstraction ne doit pas
dépendre des détails. Les détails doivent dépendre de l’abstraction.
Les classes de bas niveau implémentent des traitements basiques comme utiliser le
disque, transférer des données sur un réseau, se connecter à une base de données, etc.
Les classes de haut niveau contiennent la logique métier qui indique aux classes de
bas niveau ce qu’elles doivent faire.
1. Pour commencer, vous devez décrire les interfaces pour les traitements de bas
niveau dont les classes de haut niveau vont avoir besoin, de préférence en utilisant
les termes de la logique métier.
a. Par exemple, la logique métier doit appeler une méthode ouvrirRapport(fichier) plutôt qu’une suite de
méthodes ouvrirFichier(x) , lireOctets(n) ,fermerFichier(x) . Ces interfaces font partie du haut niveau.
2. Maintenant, vous pouvez rendre les classes de haut niveau dépendantes de ces
interfaces, plutôt que de les rendre dépendantes des classes concrètes de bas
niveau. Cette dépendance sera bien plus faible que celle d’origine.
3. Une fois que les classes de bas niveau implémentent ces interfaces, elles
deviennent dépendantes de la logique métier, inversant la direction de la
dépendance originale.
97
Dependency Inversion Principle - Exemple
98
Références
1. https://refactoring.guru/fr/design-patterns
2. https://springframework.guru/gang-of-four-design-patterns/
3. http://fr.slideshare.net/mohamedyoussfi9
4. http://www.newthinktank.com/videos/design-patterns-tutorial/
5. Alexander Shvets. “Plongée au cœur des patrons de conception.”, v2021-1.6.
99