Vous êtes sur la page 1sur 126

Exemple d’implémentation du

Design Pattern Adapter

Strategy
Template
Singleton
Method

Design
Adapter Patterns Composit
e

GOF

Mediator
Decorator
Proxy

Mohamed Youssfi, Laboratoire Signaux Systèmes Distribués et Intelligence Artificielle (SSDIA), ENSET Mohammedia, Université Hassan II de Casablanca
Decorator Design Pattern

Mohamed Youssfi, Enseignant Chercheur, ENSET Mohammedia, Université Hassan II de Casablanca,


Adapter Design Pattern

Mohamed Youssfi, Enseignant Chercheur, ENSET Mohammedia, Université Hassan II de Casablanca,


Par : Mohamed YOUSSFI

Design Patterns :
Lab. SSDIA, ENSET Mohammedia,
Université Hassan II de Casablanca Maroc
med@youssfi.net

Patterns Observer et Decorator

Strategy
Template
Singleton
Method

Design
Adapter Patterns Composite

GOF
Mediator Decorator

Proxy
Classification GOF des Design Patterns
Pattern Observer

Observer Observer Observable


Catégorie : Comportement State=10
subscribe()

Définition : subscribe
State
Changed
• Le pattern Observer définit une relation entre event
update(state=10)
les objets de type un à plusieurs, de façon que, notify
onStateChanged(10) Observers
lorsqu’un objet change d’état, tous ce qui en
update(state=10)
dépendent en soient informés et soient mis à onStateChanged(10)
jour automatiquement

Push to observers model


Pattern Observer

Observer Observer Observable


Catégorie : Comportement State=10
subscribe()

Définition : subscribe
State
Changed
• Le pattern Observer définit une relation entre event
update()
les objets de type un à plusieurs, de façon que, notify
onStateChanged() Observers
lorsqu’un objet change d’état, tous ce qui en
getState()
dépendent en soient informés et soient mis à update()
jour automatiquement onStateChanged()
getState()

Pop from observable model


Diagramme de classe du pattern Observer

Observé Observateur
Faites connaissance avec le pattern Observateur
Vous savez comment fonctionne un abonnement à un magazine:

1. Un éditeur se lance dans les affaires et commence à diffuser des magazines

2. vous souscrivez un abonnement

3. Chaque fois qu’il y’a une nouvelle édition, vous la recevez. Et tant que vous
êtes abonné, vous recevez les nouvelles éditions.

4. Quand vous ne vouliez pas de ces magazines, vous résiliez votre abonnement.
On cesse alors de vous les livrer.

5. Tant que l’éditeur reste en activité, les particuliers, les hôtels, les compagnies
aériennes, etc., ne cessent de s’abonner et de se désabonner.

¨ Si vous avez compris cet exemple, vous avez compris l’essentiel du


patterns Observateur, sauf que nous appelons l’éditeur le SUJET et les
abonnés les OBSERVATEURS
Faites connaissance avec le pattern Observateur
Le diagramme UML du pattern Observateur définit deux interfaces et deux classes.
• L’interface Observateur sera implémenté par toutes classes qui souhaitent avoir le rôle d’observateur.
• C’est le cas de la classe ObservateurConcret qui implémente la méthode actualiser(Observable). Cette méthode
sera appelée automatiquement lors d’un changement d’état de la classe observée.

• On trouve également une interface Observable qui devra être implémentée par les classes désireuses
de posséder des observateurs.
• La classe ObservableConcret implémente cette interface, ce qui lui permet de tenir informer ses
observateurs. Celle-ci possède en attribut un état (ou plusieurs) et un tableau d’observateurs. L’état est
un attribut dont les observateurs désirent suivre l’évolution de ses valeurs. Le tableau d’observateurs
correspond à la liste des observateurs qui sont à l’écoute.
• En effet, il ne suffit pas à une classe d’implémenter l’interface Observateur pour être à l’écoute, il faut
qu’elle s’abonne à un Observable via la méthode ajouterObservateur(Observateur).
Implémentation
La classe ObservableConcret dispose de quatre méthodes :

• ajouterObservateur(Observateur),

• supprimerObservateur(Observateur),

• notifierObservateurs()

• getEtat().

Les deux premières permettent, respectivement, d’ajouter des observateurs à l’écoute de la classe et d’en supprimer.

En effet, le pattern Observateur permet de lier dynamiquement (faire une liaison lors de l’exécution du programme par opposition à lier
statiquement à la compilation) des observables à des observateurs.

La méthode notifierObservateurs() est appelée lorsque l’état subit un changement de valeur. Celle-ci avertit tous les observateurs de cette
mise à jour.

La méthode getEtat() est un simple accesseur en lecture pour l’état. En effet, les observateurs récupèrent via la méthode
actualiser(Observable) un pointeur vers l’objet observé. Puis, grâce à ce pointeur, et à la méthode getEtat() il est possible d’obtenir la valeur
de l’état.
Implémentation
Il existe une variation possible lors de l’utilisation de ce pattern.

• TIRER : Dans la solution présentée, une référence vers l’objet observable est mis à disposition de chaque observateur.
Ainsi les observateurs peuvent l’utiliser pour appeler la méthode getEtat() et ainsi obtenir l’état de l’observable. Cette
solution est nommée « TIRER » car c’est aux observateurs, une fois avertis de l’évolution, d’aller chercher l’information
sur l’état.

• POUSSER :Mais il existe la solution inverse appelée « POUSSER ». Dans ce cas, on passe directement l’état actuel de
l’observable dans la méthode actualiser(TypeEtat). Ainsi les observateurs disposent directement de l’état.

Qu’elle est la meilleur solution entre les deux ? C’est la première parce qu’elle permet une fois de plus de lier faiblement
l’observable à ses observateurs. En effet, si l’observateur dispose d’un pointeur vers l’objet observable et que la classe
observable évolue en ajoutant un deuxième état. L’observateur souhaitant se tenir informé de ce deuxième état aura juste à
appeler l’accesseur correspondant. Alors que si on « POUSSER » il faudrait changer la signature de la méthode ce qui peut
s’avérer plus dommageable.
Interface Observable

package obs;

public interface Observable {

public void addObserver(Observer o);

public void removeObsever(Observer o);

public void notifyObservers();

}
Interface Observer

package obs;

public interface Observer {

public void update(Observable o);

}
Une implémentation de Observable
package obs;

import java.util.ArrayList; import java.util.List;

public class ObservableImpl implements Observable {

private List<Observer> observers=new ArrayList<>();

private int etat;

@Override

public void addObserver(Observer o) { observers.add(o); }

@Override

public void removeObsever(Observer o) { observers.remove(o); }

@Override

public void notifyObservers() {

for(Observer o:observers){ o.update(this); }

public int getEtat() { return etat; }

public void setEtat(int etat) { this.etat = etat; notifyObservers();}

}
Une implémentation de Observer

package obs;

public class ObserverImpl1 implements Observer{

@Override
public void update(Observable o) {
int nouvelEtat=((ObservableImpl)o).getEtat();
System.out.println("Observer 1 a reçu la nouvelle valeur de état
:"+nouvelEtat);
}
}
Une autre implémentation de Observer

package obs;

public class ObserverImpl2 implements Observer{

@Override
public void update(Observable o) {
int nouvelEtat=((ObservableImpl)o).getEtat();
System.out.println("Observer 2 Mise à jour avec état:"+nouvelEtat);
}
}
Application
package obs;
public class Application {
public static void main(String[] args) { ---- Changement d'atat -----
// Création du sujet ---- Changement d'atat -----
ObservableImpl observable=new ObservableImpl(); Observer 1 a reçu la nouvelle valeur de état :90
// Création des observateurs Observer 2 Mise à jour avec état:90
---- Changement
ObserverImpl1 o1=new ObserverImpl1(); ObserverImpl2 o2=new d'atat -----
ObserverImpl2();
// Changer l'état du sujet Observer 2 Mise à jour avec état:23
System.out.println("---- Changement d'atat -----");
observable.setEtat(70);
// Enregistrer les observateur
observable.addObserver(o1); observable.addObserver(o2);
// Changer l'état du sujet
System.out.println("---- Changement d'atat -----");
observable.setEtat(90);
// Supprimer un observateur
observable.removeObsever(o1);
//Changer l'état du sujet
System.out.println("---- Changement d'atat -----");
observable.setEtat(23);
}
}
Application
Cahier des charges
Félicitation! Votre société a été retenue pour construire notre station météorologique de dernière génération
consultable en ligne!
La station sera basée sur notre objet DonneesMeteo (brevet en cours),qui enregistre les conditions météorologique
à un moment donné (température, hygrométrie et pression atmosphérique).
Nous aimerions que vous nous créiez une application qui fournira d’abord trois affichages: conditions actuelles,
statistiques et prévisions simples, tous trois mis à jour en temps réel au fur et à mesure que l’objet DonneesMeteo acquiert les
données les plus récentes.
De plus cette station météo doit être extensible. MétéoExpress veut commercialiser une API pour que les autres
développeurs puissent réaliser leurs propres affichages et les insérer directement. Nous souhaitons que vous nous fournissiez
cette API !
MétéoExpress est convaincu d’avoir un excellent modèle métier: une fois les clients acrochés, nous prévoyons de
les facturer pour chaque affichage qu’ils utilisent.
Le meilleur est pour la fin : vous serez payé en stock options.
Nous attendons avec impatience vos documents de conception et votre première version alpha
Cordialement,
Jean-Loup Ragan, PDG MétéoExpress
P.S. Nous vous envoyons par chrono les fichiers source de DonnesMeteo
Vue d’ensemble de l’application
Les trois composants du système sont:

• La station météo : Équipement physique qui recueille les données météo sur le temps.

• L’objet DonneesMeteo : qui mémorise les données provenant de la station et actualise


l’affichage.

• L’affichage lui-même que les utilisateurs consultent pour connaître les conditions
météorologiques actuelles.
Capteur Extrait
d’humidité Les données Affiche
:DonneesMeteo Temp: 22°
Capteur de Hygro : 60
Station météo Pression :
température

Capteur de
pression Objet DonneesMeteo
Dispositif a’affichage
Ce que MétéoExpress Fournit Ce que nous implémentons
Si nous choisissons de l’accepter, notre tâche consiste à créer une application
qui utilise l’objet DonneesMeteo qui actualise ces trois affichages: conditions
actuelles, statistiques et prévisions
A l’intérieur de la classe DonneesMeteo
Le lendemain matin, les fichiers source de DonneesMeteo arrivent
comme promis.

Nous jetons un coup d’œil au code qui semble relativement simple:

DonneesMeteo Ces trois méthodes retournent les mesures les plus


récentes : température, humidité, et pression
getTemperature()
atmosphérique.
getHumidite() Nous n’avons pas besoin de savoir comment ces variables
getPression() sont affectées; l’objet DonneesMeteo sait comment
obtenir ces informations à jour.
actualiserMesures()
//Autres méthodes
/*
* Cette méthode est appelée chaque fois
* que les mesures ont été mises à jour
*/
public void actualiserMesures(){
// Votre code ici
}
Récapitulons….
La classe DonneesMeteo a des méthodes d’accès pour trois valeurs de mesures: température, hygrométrie, pression atmosphérique.

La méthode actualiserMesures() est appelée chaque fois qu’une nouvelle mesure est disponible. Nous ne savons pas comment cette
méthode est appelée, et peu importe.

Nous devrons implémenter trois affichages qui utilisent les données météorologiques:
• Un affichage des conditions actuelles

• Un affichage des statistiques

• Un affichage des prévisions.

Ces trois affichages doivent être mis à jour, chaque fois que DonnesMeteo aquiert de nouvelles données.

Le système doit être extensible:


• D’autres développeurs pourrons créer d’autres affichages personnalisés

• Les utilisateurs pourront ajouter ou retirer autant d ’éléments qu’ils le souhaitaient à l’application

• Actuellement, nous ne connaissons que les trois types d’affichages initiaux

• Conditions actuelles

• Statistiques

• prévisions
Premier essai de station météo
Voici une première possibilité d’implémentation: nous suivons l’indication des développeurs de
MeteoExpress et nous ajoutons notre code à la méthode actualiderMesures():

public class DonneesMeteo{


//déclarations des variables d’instance
public void actualiserMesures(){
float t=getTemperature();
float h=getHumidite();
float p getPression();
A vos crayons
affichageConditions.actualiser(t,h,p); D’après notre première implémentation, quels énoncés suivants sont
affichageStats.actualiser(t,h,p); vrais?
affichagePrevisions.actualiser(t,h,p); q A. Nous codons des implémentations concrètes, non des interfaces
} q B. Nous devons modifier le code pour chaque nouvel élément d’affichage
// Autres méthodes
} q C. Nous n’avons aucun moyen d’ajouter (ou de supprimer) des éléments
d’affichage dynamiquement
q D. Les éléments d’affichage n’implémentent pas une interface commune
q E. Nous n’avons pas encapsulé les parties qui varient
q F. Nous violons l’encapsulation de la classe DonneesMeteo
Qu’est ce qui cloche dans notre implémentation
Repenser à tous les concepts et les principes du chapitre
précédent.
public class DonneesMeteo{
//déclarations des variables d’instance
public void actualiserMesures(){
En codant des implémentation float t=getTemperature();
concrètes, nous n’avons aucun float h=getHumidite();
moyen d’ajouter ni de supprimer des float p getPression();
éléments sans modifier le
programme affichageConditions.actualiser(t,h,p);
affichageStats.actualiser(t,h,p);
affichagePrevisions.actualiser(t,h,p);
}
// Autres méthodes
}

Au moins, nous semblons utiliser une interface commune pour Points de variation : nous devons
communiquer avec les affichages.. Ils ont tous une méthode l’encapsuler
actualier() qui lie les valeurs de temp, humidité et pression
Faites connaissance avec le pattern Observateur
Vous savez comment fonctionne un abonnement à un magazine:
1. Un éditeur se lance dans les affaires et commence à diffuser des magazines
2. vous souscrivez un abonnement
3. Chaque fois qu’il y’a une nouvelle édition, vous la recevez. Et tant que vous
êtes abonné, vous recevez les nouvelles éditions.
4. Quand vous ne vouliez pas de ces magazines, vous résiliez votre
abonnement. On cesse alors de vous les livrer.
5. Tant que l’éditeur reste en activité, les particuliers, les hôtels, les
compagnies aériennes, etc., ne cessent de s’abonner et de se désabonner.

¨ Si vous avez compris cet exemple, vous avez compris l’essentiel


du patterns Observateur, sauf que nous appelons l’éditeur le
SUJET et les abonnés les OBSERVATEURS
Faites connaissance avec le pattern Observateur
Regardons de plus près Ob
jet
Quand les données de l’objet sO
sujet changent, les observateurs bs
er v
en sont informés. ate
urs

2 Objet Chien
Donée=2
2
2
Objet Sujet:
Objet Chat
Gère une donnée quelconque

Objet Souris
Cet objet n’est pas un
observateur : il n’est pas
informés quand les données du Les observateurs ont souscrit un abonnement (
Objet Canard sujet changent se sont enregistrés) au près du sujet pour
recevoir les mises à jour quand les données du
sujet changent. Ils peuvent eux-mêmes se
désabonner.
Pattern Observateur : définition

Le pattern observateur définit une relation entre les


objets de type un à plusieurs, de façon que, lorsqu’un
objet change d’état, tous ce qui en dépendent en soient
informés et soient mis à jour automatiquement
Pattern Observateur : Diagramme de classes
Chaque sujet peut avoir
Tous les observateurs
plusieurs observateurs
potentiels doivent
Voici l’interface Sujet les implémenter l’interface
objets utilise cette Observateur.
interface pour Cette interface n’a qu’une
s’enregistrer comme seule méthode actualiser()
observateur et pour qui est appelée quand
résilier leur abonnement l’état du sujet change

sujet observateurs

1 * Les observateurs
Un sujet concret concrets peuvent être
implémente toujours n’importe quelle classe
l’interface Sujet. qui implémente
Outre les méthode d’ajout l’interface Observateur.
et de suppression Chaque observateur
d’observateurs, le sujet s’enregistre au près du
concret implémente la Le sujet concret peut
également avoir des sujet réel pour recevoir
méthode les mises à jour;
notifierObservateurs() qui méthodes pour accéder
sert à mettre à jour tous à son état (getEtat()) et le
les observateurs chaque modifier (setEtat())
fois que l’état change.
Le pouvoir du faible couplage
Lorsque deux objets sont faiblement couplés, ils peuvent interagir sans pratiquement se connaître.
Le pattern observateur permet une conception dans laquelle le couplage entre sujet et observateurs est faible:
• Le sujet ne sait qu’une seule chose :
• L’observateur implémente une certaine interface (Observateur).
• Il n’a pas donc besoin de connaître ni la classe concrète de l’observateur, ni ce qu’il fait ni quoi que ce soit d’autre.
• Nous pouvons ajouter des observateurs à tout moment :
• Comme le sujet dépend uniquement d’une liste d’objets qui implémente l’interface Observateur (observateurs:ArrayList), nous pouvons
ajouter, supprimer, modifier des observateurs à volonté pendant l’exécution.
• Le sujet continue de fonctionner comme si rien n’était
• Nous n’avons jamais besoin de modifier le sujet pour ajouter de nouveaux types d’observateurs :
• Disons qu’une nouvelle classe concrète se présente et a besoin d’être un observateur, nous n’avons besoin d’ajouter quoi que ce soit au
sujet pour gérer ce nouveau type.
• Il suffit qu’elle implémente l’interface Observateur, et de l’enregistrer entant que observateur.
• Le sujet continue de diffuser des notifications à tous les observateurs.
• Nous pouvons réutiliser les observateurs et les sujets indépendamment les uns des autres.
• Si nous avons un autre emploi d’un sujet ou d’un observateur, nous pouvons les réutiliser sans problème par ce qu’ils sont faiblement
couplés.
• Les modifications des sujets n’affectent pas les observateurs et inversement
Le pouvoir du faible couplage

Principe de conception:

Efforcez-vous à coupler faiblement


les objets qui interagissent

4er Principe de conception

Les conceptions faiblement couplées nous permettent de construire


les systèmes OO souples, capables de faire face aux changements
par ce qu’ils minimisent l’interdépendance entre les objets.
A vos crayons
Avant de continuer, essayer d’esquisser les classes et les interfaces nécessaires pour implémenter la station
météo, notamment la classe DonneesMeteo et les classes des différents affichages.

• S’il vous faut un peu d’aide, lisez la page suivante : vos collègues sont déjà entrain de penser à la
conception de la station météo.
Conversation dans un box
Marie: Et bien, ça aide de savoir que nous utilisons le pattern Observateur.
Anne: Oui… mais comment allons nous l’appliquer.
Marie : regardons à nouveau la définition :
• Le pattern observateur définit une relation entre les objets de type un à plusieurs, de façon que,
lorsqu’un objet change d’état, tous ce qui en dépendent en soient informés et soient mis à jour
automatiquement
Marie : ça a l’air claire quand on y pense : Notre classe DonneesMeteo est le « un », et le « plusieurs », ce sont les éléments qui affichent les
différentes mesures;
Anne : c’est vrai, la classe DonneesMeteo a avidement un état… la température, l’humidité, la pression atmosphérique, et bien sûr, ces données
changent.
Marie : Oui, et quand ces données changent, il faut notifier les éléments d’affichage pour qu’ils puissent mettre à jour l’affichage en utilisant
les nouvelles mesures.
Anne : super, maintenant je crois que je vois comment appliquer le pattern Observateur à notre problème.
Marie : mais il y’a encore deux ou trois choses que je ne suis pas sûre d’avoir comprises.
Anne : Pae exemple ?
Marie : D’abord, comment faire pour que les éléments d’affichage puissent obtenir les mesures.
Conversation dans un box
Anne: Eh bien, en regardant à nouveau le diagramme de classes du pattern Observateur, si nous faisons de
l’objet DonneesMeteo le sujet et des éléments d’affichage les observateurs, alors les affichages vont
s’enregistrer eux-mêmes auprès de l’objet DonneesMeteo pour obtenir les informations dont ils auront
besoin, non ?
Marie: Oui… et une fois que la station météo est au courant de l’existence d’un élément d’affichage, elle
peut simplement appeler une méthode pour lui transmettre les mesures.
Anne: Il faut se souvenir que chaque affichage peut être différent… et c’est là que je crois que qu’intervient
une interface commune. Même si, chaque composant est d’un type différent, ils doivent implémenter tous une
même interface pour que l’objet DonneesMeteo sache comment leur transmettre les mesures.
Marie: je vois ce que tu veux dire. Chaque affichage aura par exemple une méthode actualier() que
DonneesMeteo va appeler.
Anne: Et actualiser() est définie dans une inteface commune que tous les éléments d’affichage
implémenteront.
Concevoir la station météo Tous nos composants implémentent
Tous les affichages possèdent
la méthode afficher(). Il
Voici l’interface Sujet l’interface Observateur. Ainsi le
implémentent donc cette
sujet dispose d’une interface à qui
interface
parler quand le moment est venu.

observateurs

sujet

Ce composant Les développeurs


peuvent développer
affiche les mesures leurs propres
courantes obtenues affichages
de l’objet
DonneesMeteo
DonneesMeteo implémente Cette classe affiche une
Cette classe Mémorise les
maintenant l’interface Sujet prévision en fonction de
mesures MIN, MOY et
l’indicateur du baromètre
MAX et les affiche.
Implémenter la station meteo
Commençons par les interfaces
public interface Sujet {
void enregisterObservateur(Observateur obs);
void supprimerObservateur(Observateur obs);
void notifierObservateurs();
}

public interface Observateur {


void actualiser(float t, float h, float p);

public interface Affichage {


void afficher();
}
Implémenter l’interface sujet dans DonneesMeteo
import java.util.ArrayList;
public class DonneesMeteo implements Sujet {
private float temperature;
private float humidite;
private float pression;
private ArrayList observateurs;
public DonneesMeteo(){
observateurs=new ArrayList();
}
public void enregisterObservateur(Observateur obs) { observateurs.add(obs);
}
public void supprimerObservateur(Observateur obs) {
int i=observateurs.indexOf(obs);
if(i>=0){
observateurs.remove(i);
}
}
Implémenter l’interface sujet dans DonneesMeteo (suite)
public void notifierObservateurs() {
for(int i=0;i<observateurs.size();i++){
Observateur o=(Observateur)observateurs.get(i);
o.actualiser(temperature,humidite,pression);
}
}
public void setMesures(float t, float h, float p) {
this.temperature=t;
this.humidite=h;
this.pression=p;
actualiserMesures();
}
public void actualiserMesures() {
notifierObservateurs();
}
}
Implémenter les affichages
public class AffichageConditions implements Observateur, Affichage {
private float temperature;
private float humidite;
private Sujet donneesMeteo;
public AffichageConditions(Sujet dm){
this.donneesMeteo=dm;
donneesMeteo.enregisterObservateur(this);
}
public void afficher() {
System.out.println("Conditions actuelles:"+
temperature+" degès et "+humidite+" % d'hmidité");
}
public void actualiser(float t, float h, float p) {
this.temperature=t;
this.humidite=h;
afficher();
}
}
Mettre en route la station météo
TestDonneesMeteo.java

dm:DonneesMeteo
public class TestDonneesMeteo { temp=22
public static void main(String[] args) { hum=65 ac:AffConditions

press=1020 temp=22
DonneesMeteo dm=new DonneesMeteo();
Observateurs: hum=65
AffichageConditions ac=new AffichageConditions(dm);
sujet:
dm.setMesures(22,65,1020);
dm.setMesures(25,75,1000); aa:AutreAffichage
dm.setMesures(23,30,1800); temp=22

} press=1020
sujet:
}

Conditions actuelles:22.0 degès et 65.0 % d'hmidité


Conditions actuelles:25.0 degès et 75.0 % d'hmidité
Conditions actuelles:23.0 degès et 30.0 % d'hmidité
Design Patterns Par : Mohamed YOUSSFI
Lab. SSDIA, ENSET Mohammedia,
Pattern Decorator Université Hassan II de Casablanca Maroc
med@youssfi.net

Strategy
Template
Singleton
Method

Design
Adapter Patterns Composite

GOF
Mediator Decorator

Proxy
Classification GOF des Design Patterns
Pattern Décorateur

Catégorie :

• Structure

Définition du pattern Décorateur


• Le pattern Décorateur attache dynamiquement des responsabilités supplémentaires à un
objet. Il fournit une alternative souple à la dérivation, pour étendre les fonctionnalités.

Résultat :

• Le Design Pattern permet d'isoler les responsabilités d'un objet.


Chaque composant peut être
utilisé seul ou enveloppé par Chaque Décorateur A-UN
un décorateur Enveloppe un composant, ce qui signifie qu’il a une variable
d’instance qui contient une référence à un composant.

Les décorateurs implémentent la même


interface ou classe abstraite que le composant
qu’il vont décorer

Le composant concret est


un objet auquel, nous
allons ajouter
dynamiquement un
nouveau comportement.
Il dérive de Composant
Les décorateurs ajoute généralement le nouveau comportement en effectuant
un traitement avant ou après une méthode existante dans le composant
Responsabilités
ComposantAbstrait :
• Définit l'interface ou une classe abstraite qui représente le composant abstrait
à décorer.
ComposantConcret :
• Implémentation de l’interface qui représente le composant concret à décorer et
qui correspondant aux fonctionnalités souhaitées à la base.
DecorateurAbstrait :
• Interface ou classe abstraite qui définit le décorateur abstrait et contient une
référence vers un objet Abstraction.
DecorateurConcretImpl1 et DecorateurConcretImpl2 :
• Représentent les décorateurs concrets des composants
• Les décorateurs ont un constructeur acceptant un objet ComposantAbstrait.
• Les méthodes des décorateurs appellent la même méthode de l'objet
qui a été passée au constructeur.
• La décoration ajoute des responsabilités en effectuant des opérations
avant et/ou après cet appel.
Responsabilités
La partie cliente manipule un objet
Abstraction.
En réalité, cet objet Abstraction peut être
• un objet ComposantConcret
• ou un objet DecorateurConcret.
• Ainsi, des fonctionnalités supplémentaires peuvent
être ajoutées à la méthode d'origine.
• Ces fonctionnalités peuvent être par exemple des
traces de log ou une gestion de buffer pour des
entrées/sorties.

med@youssfi.net
Implémentation
ComposantAbstrait.java :
public interface ComposantAbstrait {
public void operation();
}

ComposantConcretImpl1.java :
public class ComposantConcretImpl1 implements ComposantAbstrait {
@Override
public void operation() {
System.out.println("Je sais faire uniquement ça Version 1");
}
}

ComposantConcretImpl2.java :
public class ComposantConcretImpl2 implements ComposantAbstrait {
@Override
public void operation() {
System.out.println("Je sais faire uniquement ça Version 2");
}
}
Implémentation
DecorateurAbstrait.java :
public abstract class DecorateurAbstrait implements ComposantAbstrait {
protected ComposantAbstrait composantAbstrait;
public DecorateurAbstrait(ComposantAbstrait composantAbstrait) {
super();
this.composantAbstrait = composantAbstrait;
}
}

DecorateurConcretImpl1.java :
public class DecorateurConcretImpl1 extends DecorateurAbstrait {
public DecorateurConcretImpl1(ComposantAbstrait composantAbstrait) {
super(composantAbstrait);
}
@Override
public void operation() {
System.out.println("Décorateur 1 : avant, je je fais X");
composantAbstrait.operation();
System.out.println("Décorateur 1 : après, je je fais Y");
}
}
Implémentation

DecorateurConcretImpl2.java :

public class DecorateurConcretImpl2 extends DecorateurAbstrait {


public DecorateurConcretImpl1(ComposantAbstrait composantAbstrait) {
super(composantAbstrait);
}
@Override
public void operation() {
System.out.println("Décorateur 2 : avant, je fais A");
composantAbstrait.operation();
System.out.println("Décorateur 2 : après, je fais B");
}
}

med@youssfi.net
Utilisation du pattern Décorateur
ComposantAbstrait c=new ComposantConcretImpl1();
c.operation();

c :ComposantConcretImpl1

c=new DecorateurConcretImpl1(c);
c.operation();

c :DecorateurConcretImpl1 :ComposantConcretImpl1

c=new DecorateurConcretImpl2(c);
c.operation();

c :DecorateurConcretImpl2 :DecorateurConcretImpl1

:ComposantConcretImpl1
Utilisation du pattern Décorateur
public class Application {
public static void main(String[] args) {
ComposantAbstrait composantAbstrait=new ComposantConcretImpl1();
composantAbstrait.operation();
System.out.println("--------------------"); System.out.println("Première décoration");
System.out.println("--------------------");
composantAbstrait=new DecorateurConcretImpl1(composantAbstrait);
composantAbstrait.operation();
System.out.println("--------------------"); System.out.println("Deuxième décoration");
System.out.println("--------------------");
composantAbstrait=new DecorateurConcretImpl2(composantAbstrait);
composantAbstrait.operation();
}} Je sais faire uniquement ça Version1
--------------------
Première décoration
--------------------
Décorateur 1 : avant, je je fais X
Je sais faire uniquement ça Version1
Décorateur 1 : après, je je fais Y
--------------------
Deuxième décoration
--------------------
Décorateur 2 : avant, je je fais A
Décorateur 1 : avant, je je fais X
Je sais faire uniquement ça Version1
Décorateur 1 : après, med@youssfi.net
je je fais Y
Décorateur 2 : après, je je fais B
Application

med@youssfi.net
Bienvenue chez StarbuzzCoffee
Starbuzz Coffee s’est fait un nom en devenant la plus importante chaîne de « salons de café »
aux états unis.
Quand ils ont commencé, ils on conçu leurs classes comme ceci:

La variable d’instance description est


Boisson est une classe Boisson définie dans chaque sous-classe et
abstraite sous-classée par contient une description de la
description
toutes les boissons boisson, par exemple « Excellent et
getDescription()
proposées dans le café corsé »
cout()
La méthode cout() est // autres méthodes Chaque sous-classe implémente
abstraite; les sous-classes cout() pur retourner le cout de la
définir leur propre boisson
implémentation
Colombia Sumatra Espresso
Deca

cout() cout() cout()


cout()
Bienvenue chez StarbuzzCoffee

En plus de votre café, vous pouvez également demander


plusieurs ingrédients, comme
• de la mousse de lait,
• du caramel,
• du chocolat,
• du sirop,
• de la vanille
• ou noisette
• et couronner le tout avec de la crème chantilly.
Starbuzz Coffee, facturant chacun de ces suppléments, ils ont
besoin de les intégrer à leur système de commande
Première solution à ne pas faire!

Boisson
Et bien!
description
Voila ce que l’on appelle
getDescription()
« une explosion combinatoire »
cout()
// autres méthodes

ColombiaChocolat EspressoLaitCaramml
ColombiaLaitChocolatCaramel EspressoLaitChocolatEspresso
cout() cout() EspressoEspressoChantilly
cout()
SumatraLaitChocolat cout() cout()

cout() cout()
SumatraLaitCaramelChantilly
SumatraLaitCaramelChantilly
SumatraLaitCaramelChantilly
SumatraLaitCaramelChantilly
SumatraLaitCaramelChantilly
cout()
Chaque méthode cout() calcule le
cout()
cout()cout() coût du café plus celui des
cout()cout()
ingrédients de la commande
Une proposition pour éviter l’explosion de classes
Boisson
description:String
lait:boolean
caramel:boolean
chocolat:boolean
chantilly:boolean
getDescription()
cout() // non
abstraite
// autres méthodes

Colombia Sumatra Espresso


Deca

cout() cout() cout()


cout()
Première implémentation à critiquer
/*Boisson.java*/ /*Espresso.java*/
public class Boisson { public class Espresso extends
protected String description; Boisson {
protected boolean lait; public Espresso() {
protected boolean caramel; description="Espresso";
protected boolean chocolat; }
protected boolean chantilly; @Override
public double cout() {
public double cout(){ return super.cout()+6;
}
double prixIngerdients=0;
}
if(lait==true) prixIngerdients+=2;
if(caramel==true) prixIngerdients+=1;
if(chocolat==true) prixIngerdients+=1.5; /*Application.java*/
if(chantilly==true) prixIngerdients+=0.5; Espresso boisson=new Espresso();
return prixIngerdients; System.out.println(boisson.cout());
} boisson.setCaramel(true);
// Getters et Setters boisson.setChantilly(true);
} System.out.println(boisson.cout());
A vos crayons!
Quelles sont les exigences et les autres facteurs qui pourraient
changer et avoir un impact négatif sur cette conception?
• Application ouverte à la modification pour chaque ajout de nouveau
ingrédients
• Attribuer des fonctionnalités non appropriées aux classes dérivées
• Impossibilité de multiplier l’application des ingrédients
• -
• -
• -
• -
• -
• -
Principe Ouvert - Fermé
Principe de conception:

Les classes doivent être ouvertes à


l’extension et fermées à la
modification

5ème Principe de conception

Notre but est de permettre d’étendre facilement les classes pour incorporer
de nouveaux comportements sans modifier le code existant.

Qu’obtiendrons-nous, si nous y parvenons?

• Des conceptions résistantes au changement et suffisamment souples pour accepter de


nouvelles fonctionnalités répondant à l’évolution des besoins.
Faites connaissance du « Pattern Décorateur »
Nous avons vu que représenter notre boisson plus le schéma de tarification des
ingrédients au moyen de l’héritage n’a pas bien fonctionné; nous obtenons
• une explosion de classes,
• une conception rigide,
• ou nous ajoutons à la classe de base des fonctionnalités non appropriée pour certaines sous-
classes.
Voici ce que nous allons faire.
• Nous allons commencer par une boisson
• et nous allons la décorer avec des ingrédients au moment de l’exécution.
Si, par exemple, le client veut un Sumatra avec Chocolat et Chantilly, nous allons:
• Créer un objet Sumatra
• Le décorer avec un objet Chocolat
• Le décorer avec un objet Chantilly
• Appeler la méthode cout() et nous appuyer sur la délégation pour ajouter les coûts des
ingrédients.
Construire une boisson avec des décorateurs
Commençons par créer un objet Sumatra
• Boisson b1=new Sumatra();

:Sumatra
b1
cout()

n Le client veut ajouter du chocolat. Nous créer donc un objet Chocolat et


nous enveloppons le Sumatra dedans.
¨ b1=new Chocolat(b1);

:Chocolat :Sumatra
b1
cout() cout()

n Le Client veut aussi du Chantilly. Nous créons un décorateur Chantilly et


nous enveloppons Chocolat dedans.
¨ B1=new Chantilly(b1);

:Chantilly :Chocolat :Sumatra


b1
cout() cout() cout()
Diagramme de classes
Boisson 1 composant
Boisson joue le rôle de notre
classe abstraite description
getDescription()
cout()
//Autres méthodes

DecorateurIngredient
getDescription()

Colombia Sumatra
Espresso Deca
cout() cout()
cout() cout()

Les quatre composants concrets :


un par type de café
Lait Chocolat Caramel Chantilly
boisson:Boisson boisson:Boisson boisson:Boisson boisson:Boisson
cout() cout() cout() cout()
getDescription() getDescription() getDescription() getDescription()

Et voici nos décorateurs pour les ingrédients remarquez qu’ils implémentent cout() et getDescription()
Implémentation de Starbuzz
Boisson.java
public abstract class Boisson {
String description;

public String getDescription() {


return description;
}
public abstract double cout();
}

DecorateurIngredient.java
public abstract class DecorateurIngredient extends Boisson {
protected Boisson boisson;
public DecorateurIngredient(Boisson boisson) {
this.boisson = boisson;
}
public abstract String getDescription();
}
Coder les boissons
Espresso.java
public class Espresso extends Boisson {
public Espresso(){
description="Espresso";
}
public double cout() {
return 1.99;
}
}

Columbia.java
public class Colombia extends Boisson {
public Colombia(){
description="Colombia";
}
public double cout() {
return .89;
}
}
Coder les ingrédients (Décorateurs)
Chocolat.java
public class Chocolat extends DecorateurIngredient {
public Chocolat(Boisson boisson) { super(boisson); }
public double cout() { return 0.20 +boisson.cout() ;
}
public String getDescription() {
return boisson.getDescription()+", Chocolat";
}
}

Caramel.java
public class Caramel extends DecorateurIngredient {
public Caramel(Boisson boisson) { super(boisson); }
public double cout() { return 0.22 +boisson.cout() ; }
public String getDescription() {
return boisson.getDescription()+", Caramel";
}
}
Le café est servi

public class ApplicationDecorateur {

public static void main(String[] args) {


Boisson b1=new Espresso();
System.out.println(b1.getDescription()+" € "+b1.cout());

Boisson b2=new Colombia();


b2=new Caramel(b2);
b2=new Chocolat(b2);
b2=new Chocolat(b2);
System.out.println(b2.getDescription()+" € "+b2.cout());
}
}
A vos crayons : Dessinez vos objets
Exercice
InputStream du package ja.io est une classe abstraite dont plusieurs implémentations concrètes définissent une méthode read() qui
permet de lire un entier à partir d’une entrée quelconque.
La classe Concrète FileInpustream est un InputStream qui permet de lire des entiers à partir d’un fichier :
• Pour créer l’objet:
• InputStream is=new FileInputStream("original.txt");
• Pour lire un entier du fichier:
• int c=is.read();

Nous souhaitons créer un décorateur de InputStream qui nous permet de décrypter un fichier qui contient une suite de nombres paires.
Le décryptage consiste à récupérer du fichiers deux nombre par deux nombres en faisant la différence entre les deux.
• Exemple : Si le fichier contient le texte : bacadb
• Le nombre décrypté est obtenu comme suit:
• code ascii (b)-code ascii(a) = 1
• code ascii (c)-code ascii(a) = 2
• code ascii (d)-code ascii(b) = 2
• Le nombre décrypté est 122.
• Pour créer l’objet DecrypteInputStream, on peut écrire
• is=new DecrypteurInputStream(is);
• Pour lire un nombre décrypté
• int c=is.read();
Travail à faire
1. Créer un diagramme de classes
2. Créer un décorateur abstrait de InpuStream : DecorateurInputStream.java
3. Créer un Décorateur concret de InputStream, qui permet de décrypter un InputStream :
DecrypteInputStream.java
4. Créer un fichier texte « original.txt » qui contient le texte crypté
5. Créer une application qui permet de:
a. Créer un FileInputStream représentant le fichier « Original.txt »
b. Décorer l’objet créé par un objet de DecrypteInputStream
c. Afficher le texte décrypté
Design Patterns Par : Mohamed YOUSSFI
Lab. SSDIA, ENSET Mohammedia,

Pattern Composite Université Hassan II de Casablanca Maroc


med@youssfi.net

Strategy
Template
Singleton
Method

Design
Adapter Patterns Composite

GOF
Mediator Decorator

Proxy
Design Patterns du GoF (Gang of Four ) (Gamma, Helm, Johnson, Vlissides)
Pattern Composite

Catégorie :
• Structure
Objectif du pattern Composite
• Organiser les objets en structure arborescente afin de représenter une hiérarchie.
• Permettre à la partie cliente de manipuler un objet unique et un objet composé de la
même manière.
Résultat :
• Le Design Pattern permet d'isoler l'appartenance à un agrégat.
Pattern Composite
Exemples
• Un dossier peut contenir des fichiers ou des
dossiers
• Un Menu contient des éléments de menus ou
d’autres sous menus
Exemples
• Un élément HTML est formé par des attributs et
d’autres éléments
• Une forme peut être soit un rectangle, un cercle,
une ligne ou un groupe de formes
Raison d’utilisation

• Le système comporte une hiérarchie avec un nombre de niveaux non


déterminé.
• Il est nécessaire de pouvoir considérer un groupe d'éléments comme un
élément unique.
• Cela peut être le cas des éléments graphiques d'un logiciel de DAO.
Plusieurs éléments graphiques peuvent être regroupés en un nouvel élément
graphique.
• Chaque élément est un composant potentiel. En plus des éléments
classiques, il y a un élément composite qui peut être composés de plusieurs
composants.
• Comme l'élément composite est un composant potentiel, il peut être
composé d'autres éléments composites.
Responsabilités
• Composant :
• Définit l'interface d'un objet pouvant être un composant d'un autre objet de
l'arborescence.

• Element :
• implémente un objet de l'arborescence n'ayant pas d'objet le composant.

• Composite :
• implémente un objet de l'arborescence ayant un ou des objets le composant.
• La partie client manipule les objets par l'interface Composant.
Implémentation
/* Composant.java */ public abstract class Composant {
protected String nom;
protected int level;
public Composant(String nom) {
this.nom = nom;
}
public abstract void operation();
}

/* Element.java */ public class Element extends Composant {


public Element(String nom) { super(nom); }
@Override
public void operation() {
String tab=""; for(int i=0;i<level;i++) tab+="--";
System.out.println(tab+"Opération sur l'élément ("+nom+")");
}
}
med@youssfi.net
Implémentation
/* Composite.java */ import java.util.ArrayList; import java.util.List;
public class Composite extends Composant {
private List<Composant> composants=new ArrayList<Composant>();
public Composite(String nom) { super(nom); }
@Override
public void operation() {
String tab=""; for(int i=0;i<level;i++) tab+="--";
System.out.println(tab+"Opération sur un composite("+nom+")");
for(Composant composant:composants)
composant.operation();
}
public void add(Composant composant){
composant.level=this.level+1; composants.add(composant);
}
public void remove(Composant composant){ composants.remove(composant); }
public List<Composant> getChilds(){ return composants; }
}
:Composite
Implémentation nom="composite l"
level= 0

:Composite
/* Application.java */ nom="composite 2"
public class Application { level= 1
public static void main(String[] args) {
:Element
Composite racine=new Composite("Composite 1"); nom="Elément 21"
Composite composite2=new Composite("Composite 2"); level= 2
racine.add(composite2);
:Element
racine.add(new Element("Elément 11"));
nom="Elément 22"
racine.add(new Element("Elément 12")); level= 2
racine.add(new Element("Elément 13"));
composite2.add(new Element("Elément 21")); :Element
nom="Elément 11"
composite2.add(new Element("Elément 22"));
level= 1
racine.operation();
}} :Element
Opération sur un composite(Composite 1)
-- Opération sur un composite(Composite 2) nom="Elément 12"
---- Opération sur l'élément (Elément 21) level= 1
---- Opération sur l'élément (Elément 22)
-- Opération sur l'élément (Elément 11)
-- Opération sur l'élément (Elément 12)
:Element
-- Opération sur l'élément (Elément 13) nom="Elément 13"
med@youssfi.net level= 1
Design Patterns Par : Mohamed YOUSSFI
Lab. SSDIA, ENSET Mohammedia,
Pattern Adapter Université Hassan II de Casablanca Maroc
med@youssfi.net

Strategy
Template
Singleton
Method

Design
Adapter Patterns Composite

GOF
Mediator Decorator

Proxy
Exemple d’implémentation du Design pattern Adapter :
Brancher un ordinateur avec interface HDMI avec un vidéo projecteur avec interface VGA
Mohamed YOUSSFI, Lab. Informatique, Intelligence Artificielle et Cyber sécurité, ENSET, Université Hassan II de Casablancamed@youssfi.net

Strategy
Template
Singleton
Method

Design
Adapter Patterns Composite

GOF
Mediator Decorator

Proxy
Pattern Adapter

Catégorie :
• Structure
Objectif du pattern
• Convertir l'interface d'une classe dans une autre interface comprise par la partie cliente.
• Permettre à des classes de fonctionner ensemble, ce qui n'aurait pas été possible à cause de
leurs interfaces incompatibles.
Résultat :
• Le Design Pattern permet d'isoler l'adaptation d'un sous-système.
Diagramme de classes

Pattern Adapter par héritage

Pattern Adapter par Composition

med@youssfi.net
Raison d’utilisation
• Le système doit intégrer un sous-système existant.
• Ce sous-système a une interface non standard par rapport au système.
• Cela peut être le cas d'un driver bas niveau pour de l'informatique
embarquée.
• Le driver fournit par le fabricant ne correspond pas à l'interface utilisée par le
système pour d'autres drivers.
• La solution est de masquer cette interface non stantard au système et de lui
présenter une interface standard.
• La partie cliente utilise les méthodes de l'Adaptateur qui utilise les méthodes
du sous-système pour réaliser les opérations correspondantes.

med@youssfi.net
Responsabilités
• Standard : définit une interface qui est identifiée comme standard dans la partie cliente.
• ImplStandard : implémente l'interface Standard. Cette classe n'a pas besoin d'être adaptée.
• ImplAdaptee : permet de réaliser les fonctionnalités définies dans l'interface Standard,
mais ne la respecte pas. Cette classe a besoin d'être adaptée.
• Adaptateur : adapte l'implémentation ImplAdaptee à l'interface Standard. Pour réaliser
l'adaptation, l'Adaptateur peut utiliser une ou plusieurs méthodes différentes de
l'implémentation ImplAdaptee pour réaliser l'implémentation de chaque méthode de
l'interface Standard.
• La partie cliente : manipule des objets Standard. donc, l'adaptation est transparente pour
la partie cliente.

med@youssfi.net
Implémentation
/* Standard.java */
public interface Standard {
public void operation(int nb1,int nb2);
}

/* ImplStandard.java */
public class ImplStandard implements Standard {
@Override
public void operation(int nb1, int nb2) {
System.out.println("Standard, Résultat est :"+nb1*nb2); /* ImplStandard.java */
}
}
public class ImplAdaptee {
public int operation2(int nb1,int nb2){
return nb1*nb2;
}
public void operation3(int nb){
System.out.println("Adaptée, Résultat="+nb);
}
} med@youssfi.net
Implémentation
/* AdaptateurHeritage.java */
public class AdaptateurHeritage extends ImplAdaptee implements Standard {
@Override
public void operation(int nb1, int nb2) {
int nb=operation2(nb1, nb2);
operation3(nb);
}
}

/* AdaptateurComposition.java */
public class AdaptateurComposition implements Standard {
private ImplAdaptee adaptee=new ImplAdaptee();
@Override
public void operation(int nb1, int nb2) {
int nb=adaptee.operation2(nb1, nb2);
adaptee.operation3(nb);
}
}
Implémentation
/* Application.java */
public class Application {
public static void main(String[] args) {
Standard standard=new ImplStandard();
standard.operation(7, 9);
Standard adaptee1=new AdaptateurHeritage();
adaptee1.operation(7, 9);
Standard adaptee2=new AdaptateurComposition();
adaptee2.operation(7, 9);
Standard, Résultat est :63
}
Adaptée, Résultat=63
} Adaptée, Résultat=63

med@youssfi.net
Design Patterns Par : Mohamed YOUSSFI
Lab. SSDIA, ENSET Mohammedia,
Pattern Proxy Université Hassan II de Casablanca Maroc
med@youssfi.net

Strategy
Template
Singleton
Method

Design
Adapter Patterns Composite

GOF
Mediator Decorator

Proxy
Design Patterns du GoF (Gang of Four ) (Gamma, Helm, Johnson, Vlissides)
Pattern Proxy
Catégorie :
• Structure
Objectif du pattern
• Fournir un intermédiaire entre la partie cliente et un objet pour contrôler les accès à ce
dernier.
Résultat :
• Le Design Pattern permet d'isoler le comportement lors de l'accès à un
objet.
Pattern Proxy

med@youssfi.net
Raison d’utilisation
• Les opérations d'un objet sont coûteuses en temps ou sont soumises à une
gestion de droits d'accès.
• Il est nécessaire de contrôler l'accès à un objet.
• Cela peut être un système de chargement d'un document. Le document est
très lourd à charger en mémoire ou il faut certaines habilitations pour
accéder à ce document.
• L'objet réel (système de chargement classique) est l'implémentation.
L'intermédiaire entre l'implémentation et la partie cliente est le proxy.
• Le proxy fournit la même interface que l'implémentation. Mais il ne charge
le document qu'en cas de réel besoin (pour l'affichage par exemple) ou
n'autorise l'accès que si les conditions sont satisfaites.

med@youssfi.net
Responsabilités
• Abstraction : définit l'interface des classes Implémentation et Proxy.
• Implémentation : implémente l'interface. Cette classe définit l'objet que l'objet Proxy
représente.
• Proxy : fournit un intermédiaire entre la partie cliente et l'objet Implémentation. Cet
intermédiaire peut avoir plusieurs buts (synchronisation, contrôle d'accès, cache, accès
distant, ...). Dans l'exemple, la classe Proxy n'instancie un objet Implémentation qu'en cas de
besoin pour appeler la méthode correspondante de la classe Implémentation.
• La partie cliente appelle la méthode operation() de l'objet Proxy.
Implémentation
/* Abstraction.java */ /* Implemantation.java */
public interface Abstraction { public class Implementation implements Abstraction {
@Override
public void operation(); public void operation() {
} System.out.println("Exécution de l'opération de
l'implémentation...");
}}
/* Proxy.java */

public class Proxy implements Abstraction {


private Implementation implementation;
@Override
public void operation() {
System.out.println("Vérification des conditions d'accès par le proxy");
implementation=new Implementation();
implementation.operation();
}}

med@youssfi.net
Implémentation
/* Application.java */
public class Client { :Client
Créer
public static void main(String[] args) { :Proxy
Abstraction proxy=new Proxy(); operation
Vérification
proxy.operation();
Créer :Implémentation
}}
operation

Vérification des conditions d'accès par le proxy


Exécution de l'opération de l'implémentation...
Design Patterns Par : Mohamed YOUSSFI
Lab. SSDIA, ENSET Mohammedia,
Template Method Université Hassan II de Casablanca Maroc
med@youssfi.net

Strategy
Template
Singleton
Method

Design
Adapter Patterns Composite

GOF
Mediator Decorator

Proxy
Design Patterns du GoF (Gang of Four ) (Gamma, Helm, Johnson, Vlissides)
Pattern Template Method

Catégorie :
• Comportement
Objectif du pattern
• Définir le squelette d'un algorithme en déléguant certaines étapes à des sous-classes.

Résultat :
• Le Design Pattern permet d'isoler les parties variables d'un algorithme.
Raisons d’utilisation :
• Une classe possède un fonctionnement global, mais les détails de son algorithme doivent être
spécifiques à ses sous-classes.
Responsabilités
• TemplateClass: définit des méthodes abstraites primitives. La classe implémente le squelette
d'un algorithme qui appelle les méthodes primitives.
• Implentation1, Implementation2 : sont des sous-classes concrète de TemplateClass. Elle
implémente les méthodes utilisées par l'algorithme de la méthode operationTemplate() de
TemplateClass.
• La partie cliente appelle la méthode de TemplateClass qui définit l'algorithme.
Design pattern Template Method

110
Implémentation
/* TemplateClass.java */ /* Implementation1.java */
package tm; package tm;
public abstract class TemplateClass { public class Implementation1 extends TemplateClass {
public int operationTemplate(){ @Override
int a=operationAbs1(); protected int operationAbs1() {
int somme=0; return 8;
for(int i=0;i<a;i++){ }
somme+=operationAbs2(); @Override
} protected int operationAbs2() {
return somme; return 12;
} }
protected abstract int operationAbs1(); }
protected abstract int operationAbs2();
}
Implémentation
/* TemplateClass.java */ /* Implementation1.java */
package tm; package tm;
public abstract class TemplateClass { public class Implementation2 extends TemplateClass {
public int operationTemplate(){ @Override
int a=operationAbs1(); protected int operationAbs1() {
int somme=0; return 90;
for(int i=0;i<a;i++){ }
somme+=operationAbs2(); @Override
} protected int operationAbs2() {
return somme; return 33;
} }
protected abstract int operationAbs1(); }
protected abstract int operationAbs2();
}
Implémentation
import tm.Implementation1;

import tm.Implementation2;

import tm.TemplateClass;

public class Application {

public static void main(String[] args) {

TemplateClass t1=new Implementation1();

System.out.println(t1.operationTemplate());

t1=new Implementation2();

System.out.println(t1.operationTemplate());

}
Design Patterns Par : Mohamed YOUSSFI
Lab. SSDIA, ENSET Mohammedia,
Pattern State Université Hassan II de Casablanca Maroc
med@youssfi.net

Strategy
Template
Singleton
Method

Design
Adapter Patterns Composite

GOF
Mediator Decorator

Proxy
Pattern State

Catégorie :
• Comportement
Objectif du pattern
• Changer le comportement d'un objet selon son état interne.
Résultat :
• Le Design Pattern permet d'isoler les algorithmes propres à chaque état
d'un objet.

med@youssfi.net
Exemple de problème

• Un avion peut être dans l'un des trois états suivants :


• soit dans le garage,
• soit sur la piste,
• soit en l'air.
• Lorsqu'il est dans le garage, la méthode sortirDuGarage permet
de passer dans l‘état "sur la piste" .
• Lorsqu'il est sur la piste, la méthode entrerAuGarage permet de
passer dans l‘état "dans le garage"
• La méthode décoller permet de passer dans l‘état "en l'air".
• Lorsqu'il est en l'air, la méthode atterrir permet de passer dans
l‘état "sur la piste".
• Les autres combinaisons Etats - Méthodes génèrent une erreur
comme par exemple, invoquer décoller lorsque l'avion est dans le
garage.

116
Design pattern Etat ou State

Le pattern Etat permet de déléguer le comportement d'un objet dans un autre objet. Cela permet de changer le
comportement de l'objet en cours d'exécution et de simuler un changement de classe.

117
Raison d’utilisation
• Un objet a un fonctionnement différent selon son état interne. Son état change selon les
méthodes appelées.
• Cela peut être un document informatique. Il a comme fonctions ouvrir, modifier, sauvegarder
ou fermer. Le
• comportement de ces méthodes change selon l'état du document.
• Les différents états internes sont chacun représenté par une classe état (ouvert, modifié,
sauvegardé et fermé).
• Les états possèdent des méthodes permettant de réaliser les opérations et de changer d'état
(ouvrir, modifier,sauvegarder et fermer). Certains états bloquent certaines opérations (modifier
dans l'état fermé).
• L'objet avec état maintient une référence vers l'état actuel. Il présente les opérations à la partie
cliente.

med@youssfi.net
Responsabilités

• ClasseAvecEtat : est une classe avec état. Son comportement change en fonction de
son état. La partie changeante de son comportement est déléguée à un objet Etat.

• Etat : définit l'interface d'un comportement d'un état.

• EtatA, EtatB et EtatC : sont des sous-classes concrètes de l'interface Etat. Elles
implémentent des méthodes qui sont associées à un Etat.

med@youssfi.net
Diagramme d’état transition
Implémentation
/* Etat.java */
public abstract class Etat {
protected ClasseAvecEtat classeAvecEtat;
public Etat(ClasseAvecEtat classeAvecEtat) {

this.classeAvecEtat = classeAvecEtat;
}
public abstract void operationEtatA();
public abstract void operationEtatB();
public abstract void operationEtatC();

public abstract void doAction();


}
Implémentation

/* ClasseAvecEtat.java */
public class ClasseAvecEtat {
private Etat etat;
public ClasseAvecEtat() { etat=new EtatA(this); }
public void operationEtatA() { etat.operationEtatA();}
public void operationEtatB() { etat.operationEtatB(); }
public void operationEtatC() { etat.operationEtatC(); }
public void doAction() { etat.doAction(); }
public Etat getEtat() { return etat; }
public void setEtat(Etat etat) { this.etat = etat; }
}
med@youssfi.net
Implémentation
/* EtatA.java */
public class EtatA extends Etat{
public EtatA(ClasseAvecEtat classeAvecEtat) {super(classeAvecEtat); }
@Override
public void operationEtatA() {
System.out.println("Classe déjà dans l'état A");
}
@Override
public void operationEtatB() {
classeAvecEtat.setEtat(new EtatB(classeAvecEtat));
System.out.println("Changement d'état de A=>B");
}
@Override
public void operationEtatC() {
System.out.println("Impossible de passer de A =>C");
}
@Override
public void doAction() { System.out.println("Etat courant : A"); }
}
Implémentation

/* EtatB.java */
public class EtatB extends Etat{
public EtatB(ClasseAvecEtat classeAvecEtat) { super(classeAvecEtat);}
@Override
public void operationEtatA() {
System.out.println("Pas de possible de passer de B vers A");
}
@Override
public void operationEtatB() {
System.out.println("Déjà dans l'état B");
}
@Override
public void operationEtatC() {
classeAvecEtat.setEtat(new EtatC(classeAvecEtat));
System.out.println("Changement d'état de B vers C");
}
@Override
public void doAction() { System.out.println("Etat courant : B"); }
}
Implémentation
/* EtatC.java */
public class EtatC extends Etat{
public EtatC(ClasseAvecEtat classeAvecEtat) { super(classeAvecEtat); }
@Override
public void operationEtatA() {
System.out.println("Changement d'état de C vers A");
classeAvecEtat.setEtat(new EtatA(classeAvecEtat));
}
@Override
public void operationEtatB() {
System.out.println("Changement d'état de C vers B");
classeAvecEtat.setEtat(new EtatB(classeAvecEtat));
}
@Override
public void operationEtatC() {
System.out.println("Déjà dans l'état C");
}
@Override
public void doAction() { System.out.println("Etat courant : C"); }
}
Implémentation
/* Application.java */

public class Application {


public static void main(String[] args) {
ClasseAvecEtat obj=new ClasseAvecEtat(); obj.doAction();
System.out.println("-------------");
obj.operationEtatA(); obj.doAction();System.out.println("-------------");
obj.operationEtatC(); obj.doAction();System.out.println("-------------");
obj.operationEtatB(); obj.doAction();System.out.println("-------------");
obj.operationEtatC(); obj.doAction();System.out.println("-------------");
obj.operationEtatA(); obj.doAction();System.out.println("-------------");
}
}
Etat courant : A Changement d'état de A=>B
------------- Etat courant : B
Classe déjà dans l'état A -------------
Etat courant : A Changement d'état de B vers C
------------- Etat courant : C
Impossible de passer de A =>C -------------
Etat courant : A Changement d'état de C vers A
------------- Etat courant : A
-------------

Vous aimerez peut-être aussi