0% ont trouvé ce document utile (0 vote)
31 vues11 pages

Design Patterns Comportementaux

Transféré par

david Zorom
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
31 vues11 pages

Design Patterns Comportementaux

Transféré par

david Zorom
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Design patterns comportementaux

Observateur et Stratégie

Enseignant :
M. KAFANDO Aly

Membres du groupe :

DABIRE A. F. Landry Stive

SOUBEIGA Aris M. H.
Design Pattern Comportemental Observé
Les Design Patterns comportementaux, en programmation, représentent des
solutions éprouvées aux problèmes récurrents liés à la communication entre les
objets et les responsabilités de ces objets. Ces design patterns se concentrent sur
la manière dont les objets interagissent et communiquent entre eux.

Def :

Le design pattern comportemental "Observer" est utilisé pour établir une relation
de type "un-à-plusieurs" entre objets, où lorsque l'état d'un objet change, tous ses
dépendants sont notifiés automatiquement.

Pourquoi le design Pattern Observé :


Imaginez que vous avez deux types d’objets : un Client et un Magasin. Le client
s’intéresse à une marque spécifique d’un produit (disons que c’est un nouveau
modèle d’iPhone) qui sera bientôt disponible dans la boutique.

Le client pourrait se rendre sur place tous les jours et vérifier la disponibilité du
produit. Mais comme le produit n’est pas encore prêt, ses allées et venues
seraient inutiles.

Se rendre au magasin ou envoyer du spam.


À la place, le magasin pourrait envoyer des tonnes d’e-mails (ce qui peut être vu
comme du spam) à leurs clients chaque fois qu’un nouveau produit est disponible.
Cette solution économiserait bien des voyages à leurs clients. En contrepartie, le
magasin risque de se mettre à dos ceux qui ne sont pas intéressés par les
nouveaux produits.

Nous nous retrouvons dans une situation conflictuelle. 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.

Comment régler cette situation conflictuelle :

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).

Le patron de conception 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. Pas d’inquiétude ! Ce n’est pas si
compliqué que cela en a l’air. En réalité, ce mécanisme est composé 1) d’un
tableau d’attributs qui stocke une liste de références vers les objets souscripteur
et 2) de plusieurs méthodes publiques qui permettent d’ajouter ou de supprimer
des souscripteurs de cette liste.

Un mécanisme de souscription qui permet aux objets individuels de s’inscrire aux


notifications des événements.

Quand un événement important arrive au diffuseur, il fait le tour de ses


souscripteurs et appelle la méthode de notification sur leurs objets.
Les applications peuvent comporter des dizaines de classes souscripteur
différentes qui veulent être tenues au courant des événements qui affectent une
même classe diffuseur. Vous n’avez sûrement pas envie de coupler le diffuseur à
toutes ces classes. De plus, certaines ne seront peut-être pas connues à
l’avance, dans les cas où votre classe diffuseur est censée pouvoir être utilisée
par d’autres personnes.

C’est pourquoi il est crucial que tous les souscripteurs implémentent la même
interface et qu’elle soit le seul moyen utilisé par le diffuseur pour communiquer
avec eux. Elle doit déclarer la méthode de notification avec un ensemble de
paramètres que le diffuseur peut utiliser pour envoyer des données contextuelles
avec la notification.

Le diffuseur envoie des notifications aux souscripteurs en appelant la méthode de


notification spécifique sur leurs objets.

De plus, les diffuseurs doivent tous suivre la même interface si votre application
en comporte plusieurs types et que vous voulez que vos souscripteurs soient tous
compatibles avec eux. Cette interface doit contenir quelques méthodes de
souscription et elle doit permettre aux souscripteurs d’observer les états du
diffuseur sans le coupler avec leurs classes concrètes.

Conséquences :
Avantages :

1. Séparation des préoccupations : Le pattern Observer permet de séparer les


aspects de gestion des événements des objets observables et observateurs,
ce qui rend le code plus modulaire et plus facile à maintenir.
2. Extensibilité : Vous pouvez facilement ajouter de nouveaux observateurs
sans avoir à modifier le code source de l'objet observé, ce qui favorise une
conception extensible.
3. Dépendance minimale : Les observateurs n'ont pas besoin de connaître les
détails de l'objet observé, réduisant ainsi les dépendances et permettant une
meilleure encapsulation.
4. Notifications asynchrones : Les observateurs peuvent être notifiés de
manière asynchrone, ce qui peut être bénéfique dans les systèmes où la
réactivité est importante.
5. Réutilisabilité : Une fois implémenté, le pattern Observer peut être réutilisé
dans différentes parties du code, ce qui favorise la réutilisation du code.

Inconvénients :

1. Complexité accrue : L'introduction du pattern Observer peut rendre le code


plus complexe, surtout si la logique d'observation est dispersée dans
plusieurs classes.
2. Problèmes de performance : Dans les systèmes à grande échelle avec de
nombreux observateurs, la notification de chaque observateur peut entraîner
des problèmes de performance. Des stratégies de gestion des notifications
doivent être mises en place pour atténuer ce problème.
3. Risques de fuites de mémoire : Si les références des observateurs ne sont
pas correctement gérées, cela peut entraîner des fuites de mémoire, car
l'objet observé conserve des références vers ses observateurs.
4. Difficulté de débogage: Lorsque plusieurs observateurs réagissent à des
changements dans l'objet observé, le débogage des interactions entre les
observateurs peut être difficile, surtout dans les systèmes complexes.
5. Surutilisation : Comme tout pattern, il existe un risque de surutilisation du
pattern Observer, ce qui peut conduire à une conception complexe et difficile
à comprendre.
§
§
Le design pattern Stratégie

Problématique

Un kiosque possède un logiciel pour gérer ses différentes activités. Avec ce


logiciel, il est également possible de faire des paiements. On peut payer soit par
Orange Money, soit par espèce, soit par virement bancaire.

La classe qui gère les paiements pourrait être comme suit :

public class Paiement {


private string type;

public void setTypeDePaiement (string type) {this.type = type;}

private void payerParOrangeMoney (int montant) { /* traitement */}


private void payerEnLiquide (int montant) { /* traitement */}
private void payerParCheque (int montant) { /* traitement */}

public void payer (int montant) {


if (this.type == null) {
System.out.println("Aucune méthode sélectionnée.");
return;
}
switch (type) {
case "orangeMoney":
this.payerParOrangeMoney(montant);
break;
case "liquide":
this.payerEnLiquide(montant);
break;
case "cheque":
this.payerParCheque(montant);
break;

// ainsi de suite
// ...
default:
System.out.println("Erreur");
break;
}
}
}

Dans la classe principale :

public class Main {


public static void main (string[] args) {
Paiement P = new Paiement();

// paiement par Orange Money


P.setTypeDePaiement("orangeMoney");
P.payer(1000000);

// paiement par liquidité


P.setTypeDePaiement("liquide");
P.payer(2000000);

// paiement par chèque


P.setTypeDePaiement("cheque");
P.payer(3000000);

// ainsi de suite
// ...
}
}

Est-ce la meilleure manière de procéder ? ​

NON !

1. Pour ajouter d'autres moyens de paiements, il faut modifier la classe


Paiement or il faudrait être fermé à la modification ( Open Close Principle
non respecté)
2. Si y a par exemple 100 moyens de paiements, la classe Paiement pourrait
très vite devenir assez longue
3. La classe Paiement a plusieurs responsabilités. Elle doit à elle seule gérer
les paiements par chèque, par liquidité, par Orange Money, et plus ...
( Single Responsibility Principle non respecté)
4. La maintenance d'un tel code est très difficile

La solution du design pattern stratégie


Stratégie est un patron de conception comportemental qui permet de définir une
famille d’algorithmes, de les mettre dans des classes séparées et de rendre leurs
objets interchangeables; tout en assurant que chaque algorithme peut évoluer
indépendamment des clients qui l’utilisent.

Le patron de conception stratégie propose de prendre une classe dotée d’un


comportement spécifique mais qui l’exécute de différentes façons (différents
algorithmes), et de décomposer ses algorithmes en classes séparées
appelées stratégies.

Appliquons le design pattern stratégie à notre cas :

Etape 1 : définir l'interface stratégie

public interface StrategiePaiement {


void payer (int montant);
}
§

Etape 2 : définir les stratégies concrètes


Dans notre cas, les stratégies concrètes sont : paiementParOrangeMoney,
paiementParCheque et paiementEnLiquide.

public class PaiementParOrangeMoney implements StrategiePaiement {


public void payer (int montant) { /* traitement */}
}

public class PaiementParCheque implements StrategiePaiement {


public void payer (int montant) { /* traitement */}
}

public class PaiementEnLiquide implements StrategiePaiement {


public void payer (int montant) { /* traitement */}
}

§
Etape 3 : définir le contexte
Le contexte est une classe qui garde une référence vers une des stratégies.
Plutôt que de s’occuper de la tâche, le contexte la délègue à l’objet stratégie
associé.

public class ContexteDePaiement {


private StrategiePaiement strategie;

public void setStrategie (StrategiePaiement strategie) {


this.strategie = strategie;
}

public void payer (int montant) {


strategie.payer(montant);
}
}
§

Etape 4 : Utilisation

public class Main {


public static void main (string[] args) {
ContexteDePaiement Contexte = new ContexteDePaiement ();

// paiement par OM
StrategiePaiement paiementOM = new PaiementParOrangeMoney();
Contexte.setStrategie(paiementOM);
Contexte.payer(1000000);

// paiement par cheque


StrategiePaiement paiementCheque = new PaiementParCheque();
Contexte.setStrategie(paiementCheque);
Contexte.payer(2000000);
}
}

De façon générale, ce code est plus lisible et facile à maintenir. En cas d'ajout
d'un nouveau moyen de paiement, on ne modifie jamais une classe existante !

Avantages et inconvénients
1. Avantages :
1. Facilités de test : Les stratégies étant encapsulées dans des classes
séparées, elles peuvent être testées indépendamment, facilitant ainsi
l'écriture de tests unitaires.
2. Flexibilité et réutilisabilité : Les algorithmes sont encapsulés dans
des classes distinctes, ce qui permet de les réutiliser dans différents
contextes. Le comportement d'un objet peut être modifié
dynamiquement en changeant de stratégie à la volée.
Nous pouvons facilement ajouter une nouvelle méthode de
paiement (par exemple, paiement par crypto-monnaie) sans
modifier les classes existantes.
3. Réduction de la complexité conditionnelle : En éliminant les longues
chaînes de conditions (if-else ou switch-case), le code devient plus
facile à lire et à maintenir.
4. Ouverture à l'extension (OCP) : on peut facilement ajouter de
nouvelles stratégies sans modifier les classes existantes. Il suffit de
créer une nouvelle classe implémentant l'interface de stratégie.
5. Respect du principe de responsabilité unique (SRP) : Chaque
classe de stratégie a une seule responsabilité : implémenter un
algorithme spécifique. Cela rend le code plus propre et plus facile à
comprendre.
2. Inconvénients
1. Augmentation du nombre de classes : Chaque nouvelle stratégie
nécessite la création d'une nouvelle classe. Dans des systèmes
complexes avec de nombreuses stratégies, cela peut entraîner une
prolifération des classes et compliquer la gestion du code.
2. Dépendance de l'utilisateur à l'interface de stratégie : Le client (le
contexte) doit connaître les différentes stratégies disponibles et
comment les utiliser. Cela peut nécessiter une certaine compréhension
de l'implémentation interne des stratégies.

Vous aimerez peut-être aussi