Les patrons de conception

Design Patterns, Object-Oriented Programming · course

Voir tous les documents en génie logiciel

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