Vous êtes sur la page 1sur 17

ATELIER LOGICIEL : TD5

Design patterns

Design patterns
En informatique, un patron de conception est un arrangement caractéristique de modules,
reconnu comme bonne pratique en réponse à un problème de conception de logiciel.
En 1995, quatre informaticiens introduisent les “design patterns” ou patron de conception.
Ils en expliquent le concept et en décrivent 23, qu’ils placent dans trois catégories
différentes.
Creational design patterns
Les patrons de création permettent de résoudre les problèmes liés à la création et à la
configuration d’objets.
● Singleton
○ Assure qu’il n’y a toujours qu’une seule instance d’une classe, en fournissant
une interface pour la manipuler
● Prototype
○ Permet de définir le genre d’objet à créer en dupliquant une instance qui sert
d’exemple (le prototype)
● Factory (fabrique)
○ Créer des famille d’objets sans spécifier la classe concrète
● Abstract Factory (fabrique abstraite)
○ Comme une fabrique classique, sauf qu’on obtient en plus un jeu d’objets
connexe
● Builder (monteur)
○ Sépare le processus de construction d’un objet du résultat obtenu
Structural design patterns
Les patrons de structure permettent de résoudre les problèmes liés à la structuration des
classes et leurs interfaces en particulier.
● Bridge (pont)
○ Découple une abstraction de son implémentation, de façon à ce qu’ils
peuvent évoluer indépendamment
● Facade (façade)
○ Fournit une interface unifiée sur un ensemble d’interfaces d’un système.
● Adapter (adaptateur) :
○ Convertir l’interface d’une classe en une autre interface exploitée par
l’application
● Composite (objet composite)
○ Compose une hiérarchie d’objets et manipule de la même manière un
élément unique, une branche ou l’ensemble de l’arbre
● Proxy
○ Substitut d’un objet, permettant de contrôler l’utilisation de ce dernier
● Flyweight (poids-mouche)
○ Un type d’objet est utilisé pour représenter une gamme de petits objets, tous
différents
● Decorator (décorateur)
○ Attache dynamiquement des responsabilités à un objet (alternative à
l’héritage)
Behavioral design patterns
Les patrons de comportement permettent de résoudre les problèmes liés aux
comportements, à l’interaction entre les classes.
● Chain of responsibility (chaîne de responsabilité)
○ Découple l’émission d’une requête de la réception, ainsi que le traitement de
cette dernière en permettant à plusieurs objets de la traiter successivement
● Command (commande)
○ Emboîte une demande dans un objet, permettant de paramétrer, mettre en
attente, journaliser et annuler des demandes
● Interpreter (interpréteur)
○ Deux composants centraux : le contexte et l’expression. Il y a également des
objets qui représentent des éléments de grammaire d’un langage de
programmation
● Iterator (itérateur)
○ Permet l’accès séquentiel aux éléments d’un ensemble sans connaître les
détails techniques du fonctionnement de l’ensemble (ex. interface
fournissant des méthodes next(), current(), etc.)
● Mediator (médiateur)
○ Un objet définit comment plusieurs objets communiquent entre eux (évite à
chacun de faire référence à ses interlocuteurs)
● Memento
○ Externalise l’état interne d’un objet sans perte d’encapsulation (ex. permet
de remettre l’objet dans l’état où il était auparavant)
● Observer (observateur)
○ Établit une relation un à plusieurs entre des objets, où lorsqu’un objet
change, plusieurs autres objets sont avisés du changement
● State (état)
○ Permet à un objet de modifier son comportement lorsque son état interne
change
● Strategy (stratégie)
○ Une famille d’algorithmes est encapsulée de manière à ce qu’ils soient
interchangeables
● Template method (patron de méthode)
○ Définit la structure générale d’un algorithme en déléguant certains passages
● Visitor (visiteur)
○ Représente une opération à effectuer sur un ensemble d’objets
Design Pattern Composite
Dans le patron de conception composite, chaque objet ou composant peut être soit un
composant individuel, soit un composant composite. On retrouve donc une collection de
composants individuels et/ou de composants composites.

Une structure d’arbre permet également de décrire les hiérarchies d’objets de ce patron, où
un noeud d’un arbre représente :
● Soit une feuille (composant individuel)
● Soit un noeud interne (racine d’un sous-arbre, composant composite)

Pour permettre de traiter les composants individuels et les composants composites de


manière identique, une interface ou une classe abstraite mère commune sera définie.
Un service (une méthode) sera directement implémenté dans les composants individuels,
tandis que les composants composites délèguent en ajoutant éventuellement un
comportement.
Exemple
Ci-dessous, un exemple de l’utilisation du patron de conception composite, avec une classe
abstraite FileSystemComponent.

Le seul paramètre est ici le nom. Il existe bien une méthode getSize() mais celle-ci étant
abstraite, son comportement devra être défini au niveau des classes filles.

La classe FileSystemLeaf représente un fichier, composant individuel d’un système de


fichiers.

On étend bien la classe de composant, sans oublier de définir le comportement de la


méthode getSize(). Le constructeur fait bien appel à celui du parent.
La classe FileSystemComposite représente un répertoire, composants composites d’un
système de fichiers.

Étant un élément composite, il peut être la racine d’un sous-arbre et avoir des enfants. Son
paramètre est donc la liste des enfants, à partir desquelles il est possible de calculer la taille
du répertoire (dans ce contexte). D’autres méthodes sont également présentes, pour
pouvoir ajouter ou supprimer des noeuds/fichiers.

Les fichiers et les répertoires héritent donc de la même classe abstraite


FileSystemComponent. Un système de fichiers peut contenir des fichiers ou des
répertoires. Chaque répertoire peut à son tour contenir des fichiers ou des répertoires.
La classe principale pour pouvoir tester tout ce beau monde ressemble à ceci :

Pour simplifier la lecture de ce code, on peut remplacer les FileSystemComposite par des
dossiers et les FileSysteamLeaf par des fichiers (dans ce contexte seulement !).

On initialise donc 3 éléments composites, soit 3 dossiers, en précisant leur nom : pour
chacun des dossiers, une liste d’enfants vide est générée.
On ajoute ensuite les deux sous-dossiers au dossier principal : la méthode add() reçoit un
FileSystemComponent, qu’étend justement la classe composite.

Aux sous-dossiers, on ajoute de nouveaux fichiers (des feuilles), pour lesquels il faut préciser
nom et taille. On peut les déclarer directement dans la méthode, ou initialiser une variable à
part. Il reste alors à créer une liste d’éléments à afficher, on choisit le dossier principal, le
premier sous-dossier et le fichier 22. On fait appel à la méthode toString() de chacun des
éléments : dans les 3 cas, on fait appel à celle du parent, qui affiche le nom ainsi que la
taille.
Alternative
Dans la structure présentée et dans l’exemple, seules les méthodes partagées par les
composants individuels et composants composites sont dans Component
(FileSystemComponent). Une alternative consiste à tout mettre dans l’interface ou la classe
abstraite Component et à ne rien faire ou lancer une exception dans l’implémentation pour
le composant individuel.

Exercice
Q : de quelle manière JavaFX incorpore le patron de conception composite ?
R : Dans le graphe de scène, qui représente la structure hiérarchique de l’interface
graphique, avec noeud origine et noeuds enfants (Stage, Scene, Layouts, composants de
l’interface, etc.).
Design Pattern Observer
Le patron de conception observateur établir une relation entre un observé et plusieurs
observateurs, telle que lorsque l’état d’un objet change, tous les objets dépendants sont
avertis qu’ils peuvent modifier leurs états. De cette manière, tous les objets observateurs
peuvent être synchronisés avec l’objet qu’ils observent.

Pour qu’un observateur soit averti du changement d’état de l’observé, il faut que celui-ci :
● Maintienne une liste de ses observateurs et que la possibilité soit donnée de
s’enregistrer/se retirer en tant qu’observateur
● Qu’à chaque modification de son état, tous ses observateurs soient notifiés.

Lorsqu’un observateur est notifié, il doit pouvoir connaître le nouvel état de l’observé pour
se mettre à jour en synchronisation avec celui-ci.
Soit l’observé doit offrir la ou les méthodes qui permettent de l’interroger, soit il envoie son
état au moment de la notification.
Loose coupling
Dans le patron de conception observateur, on parle de couplage faible ou “loose coupling”
entre le sujet d’observation et l’observateur, car ils peuvent interagir sans connaître grand
chose l’un de l’autre.

Exemple
Pour illustrer ce patron, on a créé les interfaces Observer et Observable, ainsi que la classe
SomethingWithAState, la classe abstraite StateOfSomethingView et ses classes filles qui
implémentent la méthode abstraite stateStr().

Observer

Une seule méthode doit être implémentée par l’observateur : celle permettant de se mettre
à jour.

Observable

L’observé doit pouvoir ajouter un observateur, en retirer et les notifier d’un changement
d’état.
SomethingWithAState

L’objet étant un observé, il possède un état. Lorsque ce dernier change, on doit fait appel à
la méthode qu’on a implémentée et qui permet d’avertir les observateurs du changement
(en l'occurrence, appeler leur méthode update()). On n’oublie pas d’implémenter les autres
méthodes permettant d’ajouter ou de supprimer un observateur.
StateOfSomethingView

Cette classe correspond à un observateur : elle est construite à partir de l’observé, sans
oublier de se rajouter soi-même à la liste des observateurs de l’observé. Sa méthode
update(), dans ce contexte, met à jour le texte en récupérant l’état de l’observé (cette classe
étend un Label, on peut considérer que c’est un Label observateur).
StateOfSomethingDecView et StateOfSomethingHexView

Ces deux classes étendent simplement l’observateur, avec la particularité de d’incorporer


une méthode permettant l’affichage en décimal ou en hexadécimal de l’état de l’observé.
Au niveau de l’application :

Un des paramètres est l’objet observé. On initialise simplement, le constructeur s’occupe


d'initialiser la liste vide des observateurs et de générer une valeur aléatoire. Les vues sont
ensuite initialisées avec l’observé : leur constructeur s’occupe de tout, y compris les rajouter
à la liste des observateurs de l’observé.

Le reste est simple : à chaque fois qu’on appuie sur le bouton, on appelle la méthode
changeState() de l’observé. Elle génère un nouvel état pour l’observé et notifie chacun des
observateurs listé. On appelle pour chacun des observateurs leur méthode update(), qui
dans ce contexte, change le texte par le nouvel état de l’observé.
Dans cet exemple, l’état de l’observé est un nombre aléatoire, les observateurs le
récupèrent et l’afficheront selon un certain format défini à l’avance (ici, décimal et
hexadécimal). Le bouton change s’occupe de changer l’état de l’observé (lançant ainsi la
chaîne).

Alternative
Il existe en Java une interface Observer et une classe Observable.
Exercice
Q : ajoutez d’autres observateurs à l’exemple
R : ci-dessous, une vue octale. D’autres peuvent être ajoutées de la même manière : il suffit
de modifier le comportement de stateStr() et de ne pas oublier d’ajouter l’observateur dans
la scène.

Au niveau de la classe principale, on rajoute simplement :

Vous aimerez peut-être aussi