DesignPatterns comportement H10 - Université Lavallamontagne/glo3001/DesignPatterns...Patrons de...

Preview:

Citation preview

Design Patterns(patrons de conception)

GLO-3001 Architecture logicielle

Luc LamontagneHiver2010

Patrons de comportement

• Objectifs

– Assigner les responsabilités aux différents objets qui composent un système

– Décrire l'organisation des composantes et des – Décrire l'organisation des composantes et des mécanismes de communication entre les composantes

– Faciliter l'évolution du comportement des classes

– Maintenir un couplage faible

Friday, February 05, 2010 Luc Lamontagne 2

Patrons de comportement

• Chain of Responsibility

– Passer une requête à travers une chaîne d’objets candidats jusqu’à ce que l’un d’entre eux puisse traiter la requête

• Commande– Encapsuler une requête dans un objet

• Interpréteur•

– Définir pour un langage donné une représentation de sa grammaire et un interpréteur qui utilise cette représentation

• Itérateur– Fournir une manière d’accéder séquentiellement aux éléments d’une

collection d’objets

• Médiateur– Définir un objet qui encapsule les détails des communications entre

plusieurs objets

Friday, February 05, 2010 Luc Lamontagne 3

Patrons de comportement

• Memento

– Capturer et extraire l’état interne d’un objet

• Observateur– Notifier le changement d’état d’un objet à plusieurs autres objets

• State

– Permettre à un objet de modifier son comportement lorsque son état – Permettre à un objet de modifier son comportement lorsque son état change

• Stratégie– Définir une famille d’algorithmes interchangeables qui offrent la

même interface

• Template Method

– Définir le squelette d’un algorithme et laisser les sous-classes implanter les détails des étapes

Friday, February 05, 2010 Luc Lamontagne 4

Patrons de comportement

• Visiteur– Définir de nouvelles opérations sans modifier les classes des

éléments sur lesquelles les opérations agissent

• Null Object– Fournir un objet répondant à l’interface attendue par le client

mais n’offrant aucun comportementmais n’offrant aucun comportement

Friday, February 05, 2010 Luc Lamontagne 5

Itérateur

• Fournir une manière d’accéder séquentiellement aux éléments d’une collection d’objets

– sans révéler la représentation interne de la collection

• La collection peut contenir d’autres collections•

– Exemple: Texte � Groupe de Phrases � Groupe de Mots

• Itérateur

– Interface publique pour naviguer sur les éléments du contenants

– Déjà disponible en Java • Exemple : java.util.List ou java.sql.ResultSet

Friday, February 05, 2010 Luc Lamontagne 6

Itérateur

• Solution– Encapsuler les opérations de parcours d’une collection

• Interface (java.util.ListIterator)

– Créer une classe qui implante la logique nécessaire au parcours des éléments de la collection

– Créer une méthode de la collection qui permet d’obtenir un – Créer une méthode de la collection qui permet d’obtenir un Itérateur

– Itérateur externe• Une classe externe implante les opérations de parcours de la

collection – Itérateur spécifique à la collection

• Maintient une référence sur une copie de la collection• Possible d’avoir plus d’un Itérateur sur la collection au même moment

Friday, February 05, 2010 Luc Lamontagne 7

Itérateur externe

8

Itérateur interne

9

Itérateur interne vs. externe

Friday, February 05, 2010 Luc Lamontagne 10

a) Itérateur interne b) Itérateur filtreur externe

Itérateur

• Conséquences– Permet de parcourir les éléments d’une collection

de façon standardisée

– Permet d’encapsuler les détails du parcours de la structure d’une collectionstructure d’une collection

– Permet de supporter différents types de parcours• Ordre particulier

• Filtrage des éléments

– Permet d’obtenir plus d’un point d’accès sur une collection (External Iterator)

Friday, February 05, 2010 Luc Lamontagne 11

Itérateur - Exemple

• File avec priorité

– Les éléments de la file sont associés à un indice de priorité relatif

• Indice élevé � Haute priorité

12

• Indice faible � Basse priorité

Itérateur externe - Exemple

/**

* Itérateur de collection

*/

public interface IIterator {

public void first();

public void next();

public boolean hasNext();

public Object getCurrent();

}

13

/**

* Collection d'objets (IAggregate)

*/

public interface IAggregate {

public IIterator createIterator();

}

Itérateur externe - Exemple/**

* Queue avec priorité (ConcreteAggregate)

*/

public class PriorityQueue implements IAggregate {

private Vector<Object> queue;

private Vector<Integer> values;

public PriorityQueue() {

this.queue = new Vector<Object>();

this.values = new Vector<Integer>();

14

this.values = new Vector<Integer>();

}

public void add(Object o, int value) {

// ... //

}

public void remove(int i) {

// ... ///

}

// ...

Itérateur externe - Exemple

// ...

public Object get(int i) {

// ... //

}

public int size() {

return this.queue.size();

15

return this.queue.size();

}

public IIterator createIterator() {

return new PriorityQueueIterator(this);

}

}

Itérateur externe - Exemple

/**

* Iterateur pour une queue de priorité (ConcreteIterator)

*/

public class PriorityQueueIterator implements IIterator {

private PriorityQueue queue;

private int position;

public PriorityQueueIterator(PriorityQueue queue) {

16

this.queue = queue;

position = 0;

}

public void first() {

this.position = 0;

}

// ...

Itérateur externe - Exemple

// ...

public void next() {

this.position++;

if (this.position >= this.queue.size()) {

this.position = this.queue.size() - 1;

}

}

17

public boolean hasNext() {

return this.position < this.queue.size() - 1;

}

public Object getCurrent() {

return this.queue.get(this.position);

}

}

Itérateur externe - Exemple

public class Main {

public static void main(String[] args) {

PriorityQueue queue = new PriorityQueue();

queue.add("A", 1);

queue.add("D", 4);

queue.add("C", 3);

queue.add("G", 7);

queue.add("E", 5);

queue.add("F", 6);

queue.add("B", 2);

18

queue.add("B", 2);

queue.add("H", 8);

IIterator iter = queue.createIterator();

iter.first();

while (iter.hasNext()) {

System.out.println((String)iter.getCurrent());

iter.next();

}

System.out.println((String)iter.getCurrent());

}

}

Itérateur interne - Exemple

/**

* Queue avec priorité (ConcreteAggregate)

*/

public class PriorityQueue implements IAggregate, IIterator {

private Vector<Object> queue;

private Vector<Integer> values;

private int cursor;

public PriorityQueue() {

this.queue = new Vector<Object>();

19

this.queue = new Vector<Object>();

this.values = new Vector<Integer>();

this.cursor = 0;

}

public void add(Object o, int value) {

// ... //

}

// ...

Itérateur interne - Exemple

// ...

public void remove(int i) {// ... //

}

public Object get(int i) {// ... //

}

20

public int size() {return this.queue.size();

}

public void first() {this.cursor = 0;

}

// ...

Itérateur interne - Exemple

// ...

public void next() {

this.cursor++;

if (this.cursor >= size()) {

this.cursor = size() - 1;

}

}

21

public boolean hasNext() {

return this.cursor < size() - 1;

}

public Object getCurrent() {

return get(this.cursor);

}

}

Itérateur interne - Exemple

public class Main {

public static void main(String[] args) {

PriorityQueue queue = new PriorityQueue();

queue.add("A", 1);

queue.add("D", 4);

queue.add("C", 3);

queue.add("G", 7);

queue.add("E", 5);

queue.add("F", 6);

22

queue.add("F", 6);

queue.add("B", 2);

queue.add("H", 8);

queue.first();

while (queue.hasNext()) {

System.out.println((String)queue.getCurrent());

queue.next();

}

System.out.println((String)queue.getCurrent());

}

}

Chain of Responsibility

• Passer une requête à travers une chaîne d’objets candidats jusqu’à ce que l’un d’entre eux puisse traiter la requête

• Problème• Problème– Plusieurs objets peuvent possiblement répondre à une

requête– Le client n'est pas en mesure de choisir un répondant– On veut permettre aux répondants candidats de

répondre à la requête tout en conservant un couplage faible entre le client et les candidats

Friday, February 05, 2010 Luc Lamontagne 23

Chain of Responsibility

• Solution– Créer une chaîne de répondants (handlers)

• Les répondants implantent tous la même interface

– Chaque candidat maintient une référence sur le candidat suivant de la chaîne

– Chaque candidat maintient une référence sur le candidat suivant de la chaîne

– La requête est passée au premier candidat de la chaîne

• S'il peut répondre, la requête est traitée• S'il ne peut répondre, la requête est passée au candidat

suivant

Friday, February 05, 2010 Luc Lamontagne 24

Chain of Responsibility

25

Chain of Responsibility

$400K$25K

Friday, February 05, 2010 Luc Lamontagne 26

Interface commun

Aux objets qui peuvent

approuver une

demande d’achat Chaque objet a une limite

différente d’autorisaton

$100K $200K

Chain of Responsibility

Friday, February 05, 2010 Luc Lamontagne 27

Chain of Responsibility

• Pour obtenir une autorisation :

– Créer un ensemble– Arranger l’ensemble– Faire les connections

public abstract class PRHandler {private PRHandler nextHandler;private String handlerName;public PRHandler(String name) {

handlerName = name;}public String getName() {

Structure de

la chaîne

– Faire la requête– La demande en argument – Autorisation < limite– Si autoriser � fin– Si montant > limite_max

• Refus (message)

– Sinon au suivant !

public String getName() {return handlerName;

}public void setNextHandler(PRHandler handler) {

nextHandler = handler;};public abstract boolean authorize(PurchaseRequest

request);}public PRHandler getNextHandler() {

return nextHandler;}

Friday, February 05, 2010 Luc Lamontagne 28

Processus

d’autorisation

Chain of Responsibility

• Intention– Réduire le degré de couplage entre le client et les objets qui traite la

demande

• Si plusieurs objets peuvent faire un traitement :– On donne la chance à chacun en suivant un ordre séquentiel ;

– On forme une chaîne (référence vers le prochain objet) ;– On forme une chaîne (référence vers le prochain objet) ;

– Le premier objet décide s’il traite la requête ;

– Sinon il la passe au prochain objet ;

– La requête est passé d’un objet à l’autre :• Tant qu’elle n’est pas traitée; ou

• Tant que la fin de la chaîne n’est pas atteint.

Friday, February 05, 2010 Luc Lamontagne 29

Chain of Responsibility

• Conséquences– Permet de réduire le couplage entre le client et les répondants

• Le client ne connaît pas les répondants

– Permet plus de flexibilité dans la distribution des responsabilités• Les répondants peuvent être choisis à l’exécution

• Le choix des candidats modifie le comportement global du • Le choix des candidats modifie le comportement global du système

– Tous les objets de la chaîne ont la même interface. (favorise les changements)

– Les objets qui transmettent la requête n’ont pas à connaître les capacités de leurs successeurs

• Pas besoin de savoir quel objet va traiter la requête.

– Il n’y a aucune garantie de réponse à une requête

Friday, February 05, 2010 Luc Lamontagne 30

Chain of Responsibility- Exemple

• Système de classement de courriels

– À la réception d’un nouveau courriel, une série de filtre peuvent intercepter le courriel pour le classer dans un dossier particulierclasser dans un dossier particulier

– Si aucun filtre n’intercepte le courriel, il est placé dans la boîte de réception pour être classé manuellement

Friday, February 05, 2010 Luc Lamontagne 31

Chain of Responsibility - Exemple

/**

* Filtre pour les messages reçus (AbstractHandler)

*/

public abstract class AbstractFiltre {

private AbstractFiltre sucessor = null;

public AbstractFiltre(AbstractFiltre next) {

this.sucessor = next;

}

Successeur passé

au contructeur

32

public AbstractFiltre getNextHandler() {

return this.sucessor;

}

public abstract void request(String origine, String message);

}

Chain of Responsibility - Exemple

/**

* Filtre les messages s'il s'agit de spam (ConcreteHandler1)

*/

public class FiltreSpam extends AbstractFiltre {

public FiltreSpam(AbstractFiltre next) {

super(next);

33

}

public void request(String origine, String message) {

if (message.toLowerCase().contains("spam")) {

System.out.println("Ce message est un SPAM");

} else {

getNextHandler().request(origine, message);

}

}

}

Chain of Responsibility - Exemple

/**

* Filtre pour les messages provenant de l'université Laval (ConcreteHandler2)

*/

public class FiltreUlaval extends AbstractFiltre {

public FiltreUlaval(AbstractFiltre next) {

super(next);

34

super(next);

}

public void request(String origine, String message) {

if (origine.toLowerCase().contains("ulaval")) {

System.out.println("Ce message provient de l'Université Laval");

} else {

getNextHandler().request(origine, message);

}

}

}

Chain of Responsibility - Exemple

/**

* Dernier filtre qui effectue un traitement par défaut (ConcreteHandler3)

*/

public class FiltreAutre extends AbstractFiltre {

public FiltreAutre(AbstractFiltre next) {

35

public FiltreAutre(AbstractFiltre next) {

super(next);

}

public void request(String origine, String message) {

System.out.println("Message placé dans la boîte au lettre pour triage manuel");

}

}Fin de la chaîne

Chain of Responsibility - Exemple

public class Main {

public static void main(String[] args) {

AbstractFiltre chaine = new FiltreSpam(new FiltreUlaval(new FiltreAutre(null)));

chaine.request("test@test.com", "spam: ceci est un spam !!!");

chaine.request("registraire@ulaval.ca", "Message de l'Université Laval");

36

chaine.request("registraire@ulaval.ca", "Message de l'Université Laval");

chaine.request("test@test.com", "message non filtré");

}

}

Commande

• Encapsuler une requête dans un objet• Problème

– On veut encapsuler les demandes de service dans un objet

• Permet la manipulation des requêtes• Permet la manipulation des requêtes– Ordonnancement– Groupement– Exécution différée

– On veut configurer un objet en spécifiant les actions à effectuer sur le système lors de requêtes ou d’événements

– On veut permettre les retours en arrière (undo)

Friday, February 05, 2010 Luc Lamontagne 37

Commande

• Solution– Encapsuler les requêtes dans des classes (Command)

• Elles implantent tous la même interface

• Elles sont spécifiques aux objets du système sur lesquelles elles sont effectuées (Receivers)elles sont effectuées (Receivers)

– Une classe manipule et invoque les requêtes (Invoker)

– L’ajout de nouvelles fonctionnalités se fait par la définition de nouveaux types de requêtes• Pas de modification du système

Friday, February 05, 2010 Luc Lamontagne 38

Commande

39

Commande

• Séquence d’utilisation

1. Le client crée et configure un objet Command

– Définit les opérations que la Command exécute sur le Receiver

2. Le client passe la Command à l’Invoker

40

2. Le client passe la Command à l’Invoker

3. En temps voulu, l’Invoker lance l’exécution de la Command

4. La Command exécute ses opérations sur le Receiver

Command

41

Commande

• Particularité de ce design:

– Pas d’interaction direct Invoker – Receiver

– Nouvelle fonctionnalité � nouvelle commande• Pas de modification de code (open-closed principle)

– Objet de commande offre plusieurs possibilités– Objet de commande offre plusieurs possibilités• Stocker les commandes

• Reporter ou prioriser leur exécution

• Fonction Undo ()

• Exécution conjointe de plusieurs commandes

Friday, February 05, 2010 Luc Lamontagne 42

Command• Conséquences

– Découple l’objet qui demande les services de celui qui connaît les détails des opérations nécessaires à l’exécution

– Facilite l’ajout de nouvelles opérations

43

• Pas de modification du système

– Permet de manipuler les commandes

• Ordonnancement

• Groupement

• Exécution différée

• Retour en arrière (undo)

Commande

Friday, February 05, 2010 Luc Lamontagne 44

Commande

Mauvaise approche :

class ButtonHandler implements ActionListener {public void actionPerformed(ActionEvent e) {

if (e.getActionCommand().equals(FTPGUI.EXIT)) {System.exit(1);

}if (e.getActionCommand().equals(FTPGUI.UPLOAD)) {

int index = localList.getSelectedIndex();

Trop de vérifications

de conditions !

Tout dans la même classe.int index = localList.getSelectedIndex();String selectedItem = localList.getSelectedValue().toString();((DefaultListModel) localList.getModel()).remove(index);((DefaultListModel) remoteList.getModel()).addElement(selectedItem);

}

if (e.getActionCommand().equals(FTPGUI.DOWNLOAD)) {int index = remoteList.getSelectedIndex();String selectedItem = remoteList.getSelectedValue().toString();((DefaultListModel) remoteList.getModel()).remove(index);((DefaultListModel) localList.getModel()).addElement(selectedItem);

}if (e.getActionCommand().equals(FTPGUI.DELETE)) {

…}

Friday, February 05, 2010 Luc Lamontagne 45

Tout dans la même classe.

Commande

APPROCHE COMMANDE

interface CommandInterface {

public void processEvent();

}

class UploadButton extends JButton

implements CommandInterface {

class DownloadButton extends JButton

implements CommandInterface {

public void processEvent() {

int index = remoteList.getSelectedIndex();

String selectedItem = remoteList.getSelectedValue().toString();

((DefaultListModel) remoteList.getModel()).remove(index);

((DefaultListModel) localList.getModel()).addElement(

Les boutons

implémentent

les commandes

public void processEvent() {

int index = localList.getSelectedIndex();

String selectedItem = localList.getSelectedValue().toString();

((DefaultListModel) localList.getModel()).remove(index);

((DefaultListModel) remoteList.getModel()).addElement(selectedItem);

}

public UploadButton(String name) {

super(name);

}

}

((DefaultListModel) localList.getModel()).addElement(

selectedItem);

}

public DownloadButton(String name) {

super(name);

}

}

class buttonHandler implements ActionListener {

public void actionPerformed(ActionEvent e) {

CommandInterface CommandObj =

(CommandInterface) e.getSource();

CommandObj.processEvent();

}

}

Friday, February 05, 2010 Luc Lamontagne 46

Les événements sont

associés aux commandes

Commande - Exemple

• Calculatrice de base

– Commandes = Opérations mathématiques

• Addition

• Soustraction

47

• Multiplication

• Division

– Supporte les retours en arrière

Commande - Exemple

/**

* Calculateur basic (Receiver)

*/

public class Calculateur {

private double total;

public Calculateur() {

total = 0;

}

//...

public void multiplyBy(double valeur) {

this.total *= valeur;

}

}

public void add(double valeur) {

this.total += valeur;

}

public void substract(double valeur) {

this.total -= valeur;

}

//...

public void divideBy(double valeur) {

this.total /= valeur;

}

public double getTotal() {

return this.total;

}

}

48

Commande - Exemple

/**

* Commande (ICommand)

*/

public interface ICommand {

public void execute();

public void undo();

}

49

}

Commande - Exemple

/**

* Addition

*/

public class Addition implements ICommand {

private double valeur = 0;

private Calculateur calc;

public Addition(Calculateur calc, double valeur) {

this.calc = calc;

this.valeur = valeur;

50

this.valeur = valeur;

}

public void execute() {

this.calc.add(this.valeur);

}

public void undo() {

this.calc.substract(this.valeur);

}

}

Commande - Exemple

/**

* Multiplication

*/

public class Multiplication implements ICommand {

private double valeur = 0;

private Calculateur calc;

public Multiplication(Calculateur calc, double valeur) {

this.calc = calc;

51

this.valeur = valeur;

}

public void execute() {

this.calc.multiplyBy(this.valeur);

}

public void undo() {

this.calc.divideBy(this.valeur);

}

}

Commande - Exemple

/**

* Usager qui effectue les calculs (Invoker)

*/

public class Usager {

private ArrayList<ICommand> commands = new ArrayList<ICommand>();

private int current = -1;

public void storeCommand(ICommand cmd) { this.commands.add(cmd); }

public void executeCommand() {

52

public void executeCommand() {

ICommand cmd;

try {

cmd = this.commands.get(this.current + 1);

} catch (IndexOutOfBoundsException ex) {

cmd = null;

}

if (cmd != null) {

cmd.execute();

this.current++;

}

}

Commande - Exemple

// ...

public void undoCommand() {

ICommand cmd;

try {

cmd = this.commands.get(this.current);

} catch (IndexOutOfBoundsException ex) {

cmd = null;

53

}

if (cmd != null) {

cmd.undo();

this.current--;

}

}

}

// ...

Commande - Exemple

public class Main {

public static void main(String[] args) {

Calculateur calc = new Calculateur();

// 0 + 3 = 3

ICommand cmd1 = new Addition(calc, 3);

// 3 * 5 = 15

ICommand cmd2 = new Multiplication(calc, 5);

54

// 15 - 5 = 10

ICommand cmd3 = new Soustraction(calc, 5);

// 10 / 3 = 3.33

ICommand cmd4 = new Division(calc, 3);

Usager usager = new Usager();

usager.storeCommand(cmd1);

usager.storeCommand(cmd2);

usager.storeCommand(cmd3);

usager.storeCommand(cmd4);

// ...

Commande - Exemple

// ...

System.out.println(calc.getTotal());

usager.executeCommand(); // 0 + 3 = 3

System.out.println(calc.getTotal());

usager.executeCommand(); // 3 * 5 = 15

System.out.println(calc.getTotal());

usager.executeCommand(); // 15 - 5 = 10

System.out.println(calc.getTotal());

55

System.out.println(calc.getTotal());

usager.executeCommand(); // 10 / 3 = 3.33

System.out.println(calc.getTotal());

System.out.println("Annulation des 2 dernières opérations");

usager.undoCommand(); // 3.33 * 3 = 10

usager.undoCommand(); // 10 + 5 = 15

System.out.println(calc.getTotal());

}

}

Visiteur

• Définir de nouvelles opérations sans modifier les classes des éléments sur lesquelles les opérations agissent

• Problème– On veut définir une opération qui s’applique sur les éléments

d’une structure d’objets hétérogènesd’une structure d’objets hétérogènes– On veut regrouper les différentes versions de l’opération

(spécifiques à chaque type d’objet de la structure) dans une même classe

• Une opération donnée s’implante d’une manière différente sur chacune classes de la structure

– On veut ajouter des opérations sans modifier les classes de la structure

Friday, February 05, 2010 Luc Lamontagne 56

Visiteur

• Solution– Encapsuler l’opération dans une classe (Visitor)

• Séparer les implantations des données

– Définir une interface qui spécifie, pour chacune des – Définir une interface qui spécifie, pour chacune des classes, une méthode visit(param : type) qui prend en paramètre une référence sur un objet du type de la classe

– Le Visitor implante cette interface• Chaque méthode visit() implante une version spécifique de

l’opération représentée

• Ces méthodes font appel aux services des classes de la structure

Friday, February 05, 2010 Luc Lamontagne 57

Visiteur

• Solution (suite)– Définir une interface qui spécifie une méthode

accept(visitor) qui permet l’application d’une opération, représentée par un objet Visitor, d’être appliquée

– Les classes de la structure doivent implanter cette – Les classes de la structure doivent implanter cette interface

• Cette méthode fait appel à la méthode visit() du Visitor passé en paramètre qui est spécifique au type de la classe.

• Lors de cet appel à la méthode visit(), l’objet se passe lui-même en paramètre pour permettre au Visitor d’effectuer des requêtes sur cet objet

– Une opération = Un Visitor

Friday, February 05, 2010 Luc Lamontagne 58

Visitor

59

Visiteur

• Séquence d’utilisation1. La structure est créée2. Un objet Visitor, implantant une opération, est créé3. Pour appliquer l’opération à un objet de la structure,

on fait appel à la méthode accept() de l’objet en lui on fait appel à la méthode accept() de l’objet en lui passant le Visitor

4. Dans la méthode accept(), l’objet fait appel à la méthode visit() du Visitor qui est spécifique à son type

5. L’opération qu’implante le Visitor est alors lancée– Le Visitor fait appel aux méthodes de l’objet afin d’exécuter

l’opération

Friday, February 05, 2010 Luc Lamontagne 60

Visitor

61

Visiteur

• Conséquences– Facilite l’ajout de nouvelles opérations– Permet de regrouper les détails d’implantation d’une opération

applicable sur des objets hétérogènes– Permet de séparer une opération des données – Permet de parcourir des éléments hétérogènes d’une hiérarchie– Permet de parcourir des éléments hétérogènes d’une hiérarchie– Les Visitors peuvent accumuler des informations recueillies dans

différents objets visités– L’ajout de nouvelles classes sur lesquelles les opérations doivent

être effectuées est difficile• Demande de modifier tous les Visitors qui implantent l’interface

– Peut compromettre l’encapsulation d’une classe• Les objets doivent offrir une interface suffisante pour permettre aux

Visitors d’effectuer leurs opérations

Friday, February 05, 2010 Luc Lamontagne 62

Visiteur - Exemple

Friday, February 05, 2010 Luc Lamontagne 63

Visiteur - Exemple

Friday, February 05, 2010 Luc Lamontagne 64

Visiteur - Exemple

• Système de fichiers– On veut calculer la taille des éléments à partir

d’un élément du système de fichiers

– La taille des éléments• Fichier = Taille du fichier

• Dossier = Somme des tailles des éléments contenus

– Un Visitor parcours la hiérarchie à partir d’un nœud pour calculer la taille des éléments

Friday, February 05, 2010 Luc Lamontagne 65

Visiteur - Exemple

/**

* Visiteur (IVisitor)

*/

public interface IEntreeVisitor {

public void visit(Dossier d);

public void visit(Fichier f);

66

}

/**

* Élément visité (IElement)

*/

public interface IEntreeElement {

public void accept(IEntreeVisitor v);

}

Visiteur - Exemple/**

* Fichier (Leaf)

*/

public class Fichier implements IEntree, IEntreeElement {

private String nom;

private int taille;

public Fichier(String nom, int taille) {

this.nom = nom;

this.taille = taille;

}

67

public int getTaille() {

return this.taille;

}

public void print() {

System.out.println(" " + this.nom);

}

public void accept(IEntreeVisitor v) {

v.visit(this);

}

}

Visiteur - Exemple

/**

* Dossier (Composite)

*/

public class Dossier implements IEntree, IEntreeElement {

private String nom;

private Vector<IEntree> children;

public Dossier(String nom) {

this.nom = nom;

68

this.nom = nom;

this.children = new Vector<IEntree>();

}

public void add(IEntree e) {

this.children.add(e);

}

public void remove(IEntree e) {

this.children.remove(e);

}

// ...

Visiteur - Exemple

// ...

public IEntree get(int i) {

return this.children.get(i);

}

public int size() {

return this.children.size();

}

69

public void print() {

System.out.println("Dossier (" + this.nom + ")");

for (IEntree e : this.children) {

e.print();

}

}

public void accept(IEntreeVisitor v) {

v.visit(this);

}

}

Visitor - Exemple

/**

* Visite la hiérarchie et calcul la taille totale

* des éléments contenus par la racine choisie (ConcreteVisitor)

*/

public class TailleVisitor implements IEntreeVisitor {

private int taille;

public TailleVisitor() {

70

this.taille = 0;

}

public int getTaille() {

return this.taille;

}

// ...

Visitor - Exemple// ...

public void visit(Dossier d) {

// Visite les éléments contenus dans le dossier

IEntree entree;

IEntreeElement elem;

for (int i = 0; i < d.size(); i++) {

entree = d.get(i);

if (entree instanceof IEntreeElement) {

elem = (IEntreeElement)entree;

71

elem.accept(this);

}

}

}

public void visit(Fichier f) {

// Additionne la taille du fichier au total

this.taille += f.getTaille();

}

}

Visitor - Exemple

public class Main {

public static void main(String[] args) {Dossier d1 = new Dossier("Racine");d1.add(new Fichier("bonjour.txt", 100));d1.add(new Fichier("toto.txt", 55));d1.add(new Fichier("titi.txt", 85));

Dossier d2 = new Dossier("Doc");d2.add(new Fichier("guide.doc", 235));

72

d2.add(new Fichier("guide.doc", 235));

d1.add(d2);

Dossier d3 = new Dossier("Temp");d1.add(d3);

TailleVisitor visitor1 = new TailleVisitor();d1.accept(visitor1);

// 100 + 55 + 85 + 235 = 475System.out.println("Taille du dossier Racine = " + visitor1.getTaille());

// ...

Visitor - Exemple

// ...

TailleVisitor visitor2 = new TailleVisitor();

d2.accept(visitor2);

// 235System.out.println("Taille du dossier Doc = " + visitor2.getTaille());

TailleVisitor visitor3 = new TailleVisitor();

73

TailleVisitor visitor3 = new TailleVisitor();d3.accept(visitor3);

// 0

System.out.println("Taille du dossier Temp = " + visitor3.getTaille());}

}

Médiateur

• Application = communication entre plusieurs objets– Interaction direct (point à point) possible lorsque le nombre d’objets

est faible

• Si on augmente le nombre d’objets– Interactions directes difficiles à cause du nombre élevé d’objets– Interactions directes difficiles à cause du nombre élevé d’objets

– Labyrinthe de référence– Rend difficile la maintenance– Réutilisation limitée (forte dépendance)

• On veut permettre l’interaction entre les objets sans que chacun ne maintienne des références directes sur les autres objets

Friday, February 05, 2010 Luc Lamontagne 74

Médiateur

Friday, February 05, 2010 Luc Lamontagne 75

a) Sans médiateur

b) Avec médiateur

Médiateur

• Solution– Encapsuler les détails des communications entre les objets

(Collegues) dans une classe centrale (Mediator)

– Le Mediator contrôle les interactions entre les objets• S’occupe de transmettre les messages• S’occupe de transmettre les messages• Il implante une interface précise

– Permet de le remplacer par d’autre Mediators

– Les Collegues ont une seule référence • Sur le Mediator

– Les Collegues peuvent évoluer de façon indépendante

Friday, February 05, 2010 Luc Lamontagne 76

Médiateur

77

Médiateur

• Conséquences

– Élimine les références directes

– Simplifie les interactions entre les objets

– Permet d’abstraire comment les objets – Permet d’abstraire comment les objets communiquent entre eux

– Permet de centraliser le contrôle des interactions entre les objets

– Facilite l’évolution des relations entre les objets

– Facilite l’évolution et la réutilisation des objets

Friday, February 05, 2010 Luc Lamontagne 78

Médiateur

• Avantages:– Plus facile de modifier la nature des interrelations entre objets

• Modification du comportement du médiateur

• Accompli par surclassement avec fonctionnalités additionnelles

– En retirant les dépendences inter-objets de chacun des objets de l’application favorise la réutilisation.l’application favorise la réutilisation.

– Tests unitaires d’objets plus facile (moins de référence entre objets).

– La modification d’une classe n’affecte pas les autres classes• Faible couplage entre classes

Friday, February 05, 2010 Luc Lamontagne 79

Médiateur vs.façade

Médiateur

• Abstraction pour simplifier les communications

• Communications entre objetspar l’entremise du médiateur

Objets connaissent l’existence

Façade

• Abstraction pour simplifier l’interface de haut-niveau

• Communications avec le client par l’entremise de la façade

Objets ne connaissent pas • Objets connaissent l’existencedu médiateur

• Communication bi-directionnelles

• Modification d’un objet ne modifie pas les autres objets

• Modification possible par surclassement

• Objets ne connaissent pas l’existence de la façade

• Communications uni-directionnelles

• Modification d’un objet ne modifie pas le client

• Modification possible par surclassement

Friday, February 05, 2010 Luc Lamontagne 80

Mediateur - Exemple

• Application de Chat

– Chaque participant est représenté par un objet

– Il n’y a aucun lien directe entre les participants

81

– Il n’y a aucun lien directe entre les participants

– Les participants passent par le chambre de Chat

pour échanger des messages

– Les participants sont identifiés par un nom unique

Mediateur - Exemple

/**

* Mediator abstrait

*/

public abstract class AbstractMediator {

private HashMap<String, AbstractCollegue> collegues = new HashMap<String, AbstractCollegue>();

public void register(String id, AbstractCollegue collegue) {

if (!this.collegues.containsKey(id)) {

this.collegues.put(id, collegue);

82

this.collegues.put(id, collegue);

}

collegue.setMediator(this);

}

public void send(String id, Object msg) {

AbstractCollegue c = this.collegues.get(id);

if (c != null) {

c.receive(msg);

}

}

}

Mediateur - Exemple

/**

* Chambre de chat (ConcreteMediator)

*/

public class Chatroom extends AbstractMediator {

public Chatroom() {

}

83

public void send(String id, Object msg) {

System.out.println("Message pour : " + id);

super.send(id, msg);

}

}

Mediateur - Exemple

/**

* Participants abstraits

*/

public abstract class AbstractCollegue {

private AbstractMediator mediator;

public void setMediator(AbstractMediator mediator) {

this.mediator = mediator;

}

84

public void send(String id, Object msg) {

this.mediator.send(id, msg);

}

// Callback

public abstract void receive(Object msg);

}

Mediateur - Exemple

/*** Participant au chat*/public class Participant extends AbstractCollegue {

private String id;

public Participant(String id) {this.id = id;

}

85

public void send(String id, String msg) {System.out.println(this.id + " envoi : " + msg);super.send(id, msg);

}

public String getId() {return this.id;

}

/

public void receive(Object msg) {

System.out.println(this.id + " a reçu : " + (String)msg);

}

}

Mediateur - Exemple

public class Main {

public static void main(String[] args) {

Chatroom chatroom = new Chatroom();

Participant robert = new Participant("Robert");

chatroom.register(robert.getId(), robert);

Participant julie = new Participant("Julie");

86

Participant julie = new Participant("Julie");

chatroom.register(julie.getId(), julie);

Participant maya = new Participant("Maya");

chatroom.register(maya.getId(), maya);

robert.send("Julie", "Bonjour Julie!");

julie.send("Robert", "Bonjour Robert!");

robert.send("Maya", "Bonjour Maya!");

}

}

Memento

• Capturer et extraire l’état interne d’un objet

• Problème– L’état d’un objet est défini par la valeur de ses

attributsattributs– La modification des attributs entraîne un changement

d’état– On veut pouvoir sauvegarder l’état de l’objet pour

pouvoir le restaurer plus tard– On ne veut pas exposer la représentation interne de

l’objet

Friday, February 05, 2010 Luc Lamontagne 87

Memento

• Solution– Définir une classe qui conserve une copie des valeurs des attributs

(Memento)• Copie de l’état de l’objet d’origine (Originator)• Une méthode de l’Originator permet d’obtenir un objet Memento qui

représente l’état actuel de celui-ci• Le Memento est un conteneur de données• Le Memento est un conteneur de données

– Lecture seule

– Les copies des attributs conservés dans le Memento ne doivent être accessible que par l’Originator

• Utilisation d’une interface sans méthodes pour permettre aux autres objets de manipuler les Mementos sans avoir accès aux accesseurs

– Définir une classe qui conserve et organise les Mementos (Caretaker)

Friday, February 05, 2010 Luc Lamontagne 88

Memento

89

Memento

90

Memento

• Conséquences– Préserve le principe d’encapsulation

– Permet de décharger l’Originator de la gestion des versions précédentes de son état

– Peut devenir coûteux en terme de mémoire et de temps – Peut devenir coûteux en terme de mémoire et de temps de calcul si la quantité d’information est importante

– Difficile de préserver l’encapsulation de manière stricte

– Difficile d’appliquer une politique efficace de gestion des Mementos par le Caretaker

• Combien et quels Mementos conserver?

• Quand supprimer un Memento?

Friday, February 05, 2010 Luc Lamontagne 91

Memento

• Exemple de conversion de données

Identifiant du dernier

enregistrement de client

Friday, February 05, 2010 Luc Lamontagne 92

Restoration de l’état

Memento - Exemple

• Rapport de projet

– On veut conserver des sauvegardes de l’avancement d’un rapport

– On conserve

93

• Le corps du rapport (texte)

• La date de la dernière sauvegarde

• Le nom du dernier auteur

Memento - Exemple/**

* Rapport de projet (Originator)

*/

public class Rapport {

private String auteur;

private String corps;

private GregorianCalendar date;

public Rapport(String auteur) {

this.auteur = auteur;

this.corps = "";

this.date = new GregorianCalendar();

94

this.date = new GregorianCalendar();

}

public String getAuteur() { return this.auteur;

}

public void setAuteur(String auteur) {this.auteur = auteur;

}

public String getCorps() {return this.corps;

}

//...

Memento - Exemple

// ...

public void setCorps(String texte) {this.corps = texte;

}

public IMementoHandling createMemento() {

IMementoHandling m = new Memento(this.auteur, this.corps, this.date);

this.date = new GregorianCalendar();

95

this.date = new GregorianCalendar();

return m;

}

public void restore(IMementoHandling m) {

Memento previous = (Memento)m;

this.auteur = previous.getAuteur();

this.corps = previous.getCorps();

this.date = previous.getDate();

}

}

Memento - Exemple

/**

* Interface de manipulation des Mementos

* par le Caretaker

*/

public interface IMementoHandling {

}

96

Memento - Exemple/**

* Sauvegarde de l'état interne

*/

public class Memento implements IMementoHandling {

private final String auteur;

private final String corps;

private final GregorianCalendar date;

public Memento(String auteur, String corps, GregorianCalendar date) {

this.auteur = auteur;

this.corps = corps;

this.date = date;

97

this.date = date;

}

public String getAuteur() {

return this.auteur;

}

public String getCorps() {

return this.corps;

}

public GregorianCalendar getDate() {

return this.date;

}

}

Memento - Exemple/**

* Historique des sauvegardes (Caretaker)

*/

public class Historique {

private ArrayList<IMementoHandling> states;

public Historique() {

this.states = new ArrayList<IMementoHandling>();

}

98

public void add(IMementoHandling m) {

this.states.add(m);

}

public IMementoHandling getMemento(int i) {

if (i < this.states.size()) {

return this.states.get(i);

} else {

return null;

}

}

}

Memento - Exemplepublic class Main {

public static void main(String[] args) {

Rapport rpt = new Rapport("Roger Lagrange");

Historique hist = new Historique();

System.out.println(rpt.getAuteur() + " [" + rpt.getCorps() + "]");

rpt.setCorps("Bonjour le monde!");

hist.add(rpt.createMemento());

System.out.println(rpt.getAuteur() + " [" + rpt.getCorps() + "]");

99

System.out.println(rpt.getAuteur() + " [" + rpt.getCorps() + "]");

rpt.setAuteur("Samuel Champagne");

rpt.setCorps("Bonjour à tout le monde!");

hist.add(rpt.createMemento());

System.out.println(rpt.getAuteur() + " [" + rpt.getCorps() + "]");

System.out.println("--- Retour à la première sauvegarde ---");

rpt.restore(hist.getMemento(0));

System.out.println(rpt.getAuteur() + " [" + rpt.getCorps() + "]");

}

}

Observateur

• Notifier le changement d’état d’un objet à plusieurs autres objets

• Problème• Problème

– Plusieurs composantes sont dépendantes de l’état d’un objet

– Lorsque l’état de l’objet change, les objets dépendants doivent en être notifiés

Friday, February 05, 2010 Luc Lamontagne 100

Observateur - Exemple

• Rapports de vente par département

Friday, February 05, 2010 Luc Lamontagne 101

Observateur

• Observateurs:– Un objet portant un intérêt à l’état du sujet– Doit savoir quand le sujet change d’état

• Sujet (subject): – Peut avoir plusieurs observateurs. – Construction dynamique d’une liste d’observateur par enregistrement.– Lorsqu’un changement survient, il avise les observateurs enregistrés.– Lorsqu’un changement survient, il avise les observateurs enregistrés.– Sur réception, les observateurs se synchronise avec l’état du sujet (mise à jour

de leurs attributs).– Donc le sujet est un publisher car il envoie des messages à tous les

souscripteurs (observers).– Les observateurs peuvent se désenregistrer.

Friday, February 05, 2010 Luc Lamontagne 102

Observateur

• Solution– Mettre en place un système de communication entre les objets

dépendants (Observers) et l’objet dont ils dépendent (Subject)

– Le sujet maintient une liste d’objets dépendants• Offre des services d’enregistrement et de désenregistrement• Offre des services d’enregistrement et de désenregistrement

– Les observateurs s’enregistrent auprès du sujet

– Les observateurs implantent une interface commune• Spécifie une méthode de notification (Callback)

– Lorsque l’état du sujet change, le sujet appelle la méthode de notification sur chacun des observateurs enregistrés

Friday, February 05, 2010 Luc Lamontagne 103

Observateur

• Modèle Pull– L’observateur maintient une référence sur le sujet– Le sujet offre une interface suffisante pour que les

observateurs puissent accéder aux attributs définissant son état

– Suite à une notification, l’observateur effectue des requêtes sur le sujet afin d’extraire son nouvel état

• Modèle Push– Lors d’une notification, le sujet envoi son nouvel état

aux observateurs• Paramètre de la méthode de notification (Callback)

Friday, February 05, 2010 Luc Lamontagne 104

Observateur - Pull

105

Observateur - Push

106

Observateur

• Conséquences– Permet d’assurer la consistance entre les états d’objets

dépendants– Permet d’assouplir le couplage entre le sujet et les observateurs

• Le sujet n’est pas lié aux observateurs

– Permet de notifier un changement à plusieurs classes – Permet de notifier un changement à plusieurs classes intéressées

– Permet de supporter la communication par diffusion (Broadcast)

– Peut entraîner des mises à jour en cascade– Si aucune stratégie ne permet de cibler ce qui a changé dans

l’état du sujet, les synchronisations des états des observateurs avec le sujet peut devenir très coûteuse

Friday, February 05, 2010 Luc Lamontagne 107

Observateur - Exemple

• Système de surveillance météorologique

– Des appareils prennent des mesures sur le terrain

• Thermomètre

• Baromètre

108

• …

– Des moniteurs enregistrent ou affiche les valeurs des mesures effectuées par les appareils

• Écran

• Journal des événements

Observateur (Pull) - Exemple

/**

* Observer object (IObserver)

*/

public interface IObserver {

public void update();

}

109

Observateur (Pull) - Exemple/**

* Abstract Subject (AbstractSubject)

*/

public class AbstractSubject {

private ArrayList<IObserver> observers;

public AbstractSubject() {

this.observers = new ArrayList<IObserver>();

}

110

public void register(IObserver o) {

this.observers.add(o);

}

public void unregister(IObserver o) {

this.observers.remove(o);

}

public void notifyObservers() {

for (IObserver o : this.observers) {

o.update();

}

}

}

Observateur (Pull) - Exemple/**

* Thermomètre qui donne la température d'une composante (ConcreteSubject)

*/

public class Thermometre extends AbstractSubject {

private double temperature;

public Thermometre(double temperature) {

this.temperature = temperature;

}

111

public double getTemperature() {

return this.temperature;

}

public void setTemperature(double t) {

this.temperature = t;

super.notifyObservers();

}

}

Observateur (Pull) - Exemple

/**

* Console de surveillance (ConcreteObserver)

*/

public class Console implements IObserver {

private Thermometre thermometre;

public Console(Thermometre t) {

this.thermometre = t;

112

this.thermometre = t;

}

public void update() {

double t = this.thermometre.getTemperature();

System.out.println("Console : Température actuelle = " + t);

}

}

Observateur (Pull) - Exemple

public class Main {

public static void main(String[] args) {

Thermometre thermometre = new Thermometre(0);

Console console = new Console(thermometre);

thermometre.register(console);

113

Journal journal = new Journal(thermometre);

thermometre.register(journal);

thermometre.setTemperature(5);

thermometre.setTemperature(7);

}

}

Observateur (Push) - Exemple

/**

* Observer object (IObserver)

*/

public interface IObserver {

public void update(Object state);

}

114

Observateur (Push) - Exemple/**

* Abstract Subject (AbstractSubject)

*/

public class AbstractSubject {

private ArrayList<IObserver> observers;

public AbstractSubject() {

this.observers = new ArrayList<IObserver>();

}

115

public void register(IObserver o) {

this.observers.add(o);

}

public void unregister(IObserver o) {

this.observers.remove(o);

}

public void notifyObservers(Object state) {

for (IObserver o : this.observers) {

o.update(state);

}

}

Observateur (Push) - Exemple

/**

* Thermomètre qui donne la température d'une composante (ConcreteSubject)

*/

public class Thermometre extends AbstractSubject {

private double temperature;

public Thermometre(double temperature) {

this.temperature = temperature;

116

}

public double getTemperature() {

return this.temperature;

}

public void setTemperature(double t) {

this.temperature = t;

super.notifyObservers(getTemperature());

}

}

Observateur (Push) - Exemple

/**

* Console de surveillance (ConcreteObserver)

*/

public class Console implements IObserver {

private Thermometre thermometre;

public Console(Thermometre t) {

this.thermometre = t;

}

117

public void update(Object state) {

Double t = (Double)state;

System.out.println("Console : Température actuelle = " + t);

}

}

Observateur (Push) - Exemple

public class Main {

public static void main(String[] args) {

Thermometre thermometre = new Thermometre(0);

Console console = new Console(thermometre);

thermometre.register(console);

Journal journal = new Journal(thermometre);

118

Journal journal = new Journal(thermometre);

thermometre.register(journal);

thermometre.setTemperature(5);

thermometre.setTemperature(7);

}

}

État (state)

• Permettre à un objet de modifier son comportement lorsque son état change

• Problème– L’état d’un objet est défini par la valeur des ses attributs– Une modification des attributs d’un objet amène un – Une modification des attributs d’un objet amène un

changement d’état– Le comportement d’un objet peut être influencé par son état

• Un objet implante donc différentes variantes de son comportement• Comportement choisi par des expressions conditionnelles

– On veut pouvoir changer le comportement d’un objet lorsque son état change

Friday, February 05, 2010 Luc Lamontagne 119

État (state)

• Exemple: un compte de banque dont les états sont :

– Sans frais de transaction: montant > seuil

– Avec frais de transaction : 0 < montant < seuil

– Débiteur : montant < 0– Débiteur : montant < 0

• Une transaction qui excède la limite est refusée

Friday, February 05, 2010 Luc Lamontagne 120

État (state)public class BusinessAccount {

public static final double MIN_BALANCE = 2000.00; public static final double OVERDRAW_LIMIT = -1000.00;private State objState;private String accountNumber;private double balance;

public void setState(State newState) { objState = newState; }public State getState() { return objState; }

public boolean deposit(double amount) {public boolean deposit(double amount) {return getState().deposit(amount);

}

public boolean withdraw(double amount) {return getState().withdraw(amount);

}

public BusinessAccount(String accountNum) {accountNumber = accountNum;objState = State.InitialState(this);

}public double getBalance() { return balance; }public void setBalance(double newBalance) {balance = newBalance;}

}

Friday, February 05, 2010 Luc Lamontagne 121

État (state)public class State {

private BusinessAccount context;

public BusinessAccount getContext() { return context; }

public void setContext(BusinessAccount newAccount) {context = newAccount;

} public State transitionState() { return null; }

public State(BusinessAccount account) { setContext(account); }

public static State InitialState(BusinessAccount account) {public static State InitialState(BusinessAccount account) {return new NoTransactionFeeState(account);

}

public boolean deposit(double amount) {double balance = getContext().getBalance();getContext().setBalance(balance + amount);transitionState();return true;

}

public boolean withdraw(double amount) {double balance = getContext().getBalance();getContext().setBalance(balance - amount);transitionState();return true;

}}Friday, February 05, 2010 Luc Lamontagne 122

État (state)

• Solution– Encapsuler les différents comportements que peut

prendre un objet en fonction de son état dans des classes distinctes (State)

• Les classes State implantent toutes la même interface• Les classes State implantent toutes la même interface

– Une Abstract Parent Class spécifie les méthodes de service qui sont influencées par l’état de l’objet

– La classe parent implante les méthodes de service de l’objet qui ne sont pas influencées

Friday, February 05, 2010 Luc Lamontagne 123

État (state)

• Solution (suite)– Des sous-classes implantent les méthodes abstraites qui

définissent le comportement de l’objet dans un état précis• Une sous-classe = Un état précis de l’objet

– Les requêtes sur les méthodes sensibles à l’état de l’objet sont redirigées vers la sous-classe qui représente l’état courant de l’objet

– Suite à une requête, l’état de l’objet est réévalué et une autre sous-classe est choisie pour représenter l’état courant

Friday, February 05, 2010 Luc Lamontagne 124

État (state)

125

État (state)

• Conséquences– Permet de regrouper les comportements en fonction du

contexte dans lequel le comportement est applicable– Facilite l’évolution et l’extension du comportement de l’objet– Élimine de nombreuses et complexes opérations conditionnelles– Permet de structurer le comportement d’un objet– Permet de structurer le comportement d’un objet– Rend explicite les changements d’état– Permet de partager des comportements entre plusieurs

instances d’une classe– La décentralisation de la logique des transitions d’état amène

des dépendances entre les objets State

– Augmente le nombre d’objets du système

Friday, February 05, 2010 Luc Lamontagne 126

État- Exemple

• Système de gestion d’un barrage hydroélectrique– Le barrage doit maintenir un certain niveau d’eau

– Pour contrôler le niveau d’eau, on peut• Ouvrir les valves

• Fermer les valves• Fermer les valves

– Lorsque les valves sont fermées, une accumulation naturelle s’effectue

– 3 états• Niveau bas � Fermer les valves

• Niveau normal � Ne rien faire

• Niveau élevé � Ouvrir les valves

Friday, February 05, 2010 Luc Lamontagne 127

État - Exemple/**

* Barrage hydroélectrique (Context)

*/

public class Barrage {

private AbstractState state;

private double quantite;

public Barrage() {

this.quantite = 0;

this.state = null;

128

}

public AbstractState getState() {

return this.state;

}

public void setState(AbstractState state) {

this.state = state;

}

// ...

État- Exemple// ...

public double getQuantite() {

return this.quantite;}

public void setQuantite(double qte) {

this.quantite = qte;}

public void ecouler(double qte) {

129

public void ecouler(double qte) {setQuantite(getQuantite() - qte);

}

public void accumuler(double qte) {setQuantite(getQuantite() + qte);

}

// service()public void ajusterNiveau() {

getState().ajusterNiveau();}

}

État- Exemple/**

* Abstract State

*/

public abstract class AbstractState {

public static final double MIN = 100;

public static final double MAX = 200;

private Barrage barrage;

public AbstractState(Barrage barrage) {

this.barrage = barrage;

130

this.barrage = barrage;

}

public Barrage getBarrage() {

return this.barrage;

}

public abstract void changeState(); // handle()

public abstract void ajusterNiveau(); // changeState()

}

État- Exemple/**

* Niveau d'eau normal dans le barrage (ConcreteState)

*/

public class NiveauNormal extends AbstractState {

public NiveauNormal(Barrage barrage) {

super(barrage);

}

// changeState

public void changeState() {

if (getBarrage().getQuantite() > MAX) {

131

if (getBarrage().getQuantite() > MAX) {

getBarrage().setState(new NiveauEleve(getBarrage()));

} else if (getBarrage().getQuantite() < MIN) {

getBarrage().setState(new NiveauBas(getBarrage()));

}

}

// handle

public void ajusterNiveau() {

System.out.println("Niveau normal : " + getBarrage().getQuantite());

getBarrage().accumuler(35); // accumulation naturelle

changeState();

}

}

État- Exemple/**

* Niveau d'eau élevé dans le barrage (ConcreteState)

*/

public class NiveauEleve extends AbstractState {

public NiveauEleve(Barrage barrage) {

super(barrage);

}

// changeState

public void changeState() {

if (getBarrage().getQuantite() < MIN) {

132

if (getBarrage().getQuantite() < MIN) {

getBarrage().setState(new NiveauBas(getBarrage()));

} else if (getBarrage().getQuantite() < MAX) {

getBarrage().setState(new NiveauNormal(getBarrage()));

}

}

// handle

public void ajusterNiveau() {

System.out.println("Niveau élevé : " + getBarrage().getQuantite());

getBarrage().ecouler(100); // Ouvrir les valves

changeState();

}

}

Stratégie

• Situation:– Plusieurs algorithmes applicables à un même problème

– On veut pouvoir choisir dynamiquement l’algorithme à utiliser selon les besoins actuels

• Approche • Approche – Définir une famille d’algorithmes interchangeables qui offrent

la même interface

– Chaque algorithme implanté dans une classe séparée (une stratégie)

– Les algorithmes offrent le même interface

– Séparation de l’implantation de l’algorithme et de son contexte d’utilisation

Friday, February 05, 2010 Luc Lamontagne 133

Stratégie

• Solution– Encapsuler les algorithmes dans des classes distinctes (Strategy)

• Les Strategy implantent toutes la même interface• Même service mais en utilisant des approches différentes

– L’objet client choisit l’algorithme à utiliser– L’objet client choisit l’algorithme à utiliser

– Le client doit fournir à l’algorithmes toutes les informations nécessaires ou offrir une interface suffisante pour que l’algorithme soit en mesure d’extraire lui-même les informations nécessaires

– L’algorithme est séparé de son contexte

Friday, February 05, 2010 Luc Lamontagne 134

Stratégie

135

Stratégie

• Conséquences– Permet de créer des familles d’algorithmes fournissant un

service commun– Offre une alternative à l’héritage pour supporter différents

algorithmes– Facilite l’évolution et l’ajout d’algorithmes– Facilite l’évolution et l’ajout d’algorithmes– Permet de choisir l’algorithme à l’exécution– Élimine de nombreuses opérations conditionnelles– Permet d’offrir différentes implantations d’un même

comportement– Le client doit connaître les algorithmes disponibles ainsi que la

logique permettant de sélectionner le bon algorithme– Augmente le nombre d’objets du système

Friday, February 05, 2010 Luc Lamontagne 136

Stratégie

• Avantages– Évite les séquences de conditions (open-closed principle)

– Plus simple et facile d’ajouter, d’enlever ou modifier un algorithme

– Éviter de dériver des classes qui comportent le contexte et – Éviter de dériver des classes qui comportent le contexte et l’algorithme

• Évite de faire une assocation statique entre un algorithme et un contexte

• Facilite le changement du comportement d’un contexte

• Évite les explosions combinatoires de sous-classes

Friday, February 05, 2010 Luc Lamontagne 137

Stratégie vs. State

• Choix:

– State : dépend de l’état du context objet

– Stratégie : dépend des besoins de l’application

• Context object:• Context object:

– State : peut changer d’état, les transitions sont bien définies par les caractéristiques d’une application

– Stratégie : ne change pas d’état.

Friday, February 05, 2010 Luc Lamontagne 138

Stratégie - Exemple

• Système permettant le cryptage de messages– Plusieurs algorithmes de cryptage

– Le client peut choisir l’algorithme à utiliser

Décalage (shift) d’un

caractère

Substitution de caractères à

partir d’une table de

139

caractère

Ex: hello � elloh

partir d’une table de

correspondance

Ex: hello � WXCCB

Substitution de mots à partir

d’une table

Ex: hello � toto

Rotation d’un caractère

Ex: hello � ifmmp

Context object

Stratégie - Exemple

/**

* Interface des stratégies (IStrategy)

*/

public interface IStrategy {

public String encrypter(String message);

public String decrypter(String message);

}

140

Stratégie - Exemple/**

* Rotation-Substitution (ConcreteStrategy)

*/

public class Caesar implements IStrategy {

public String encrypter(String message) {

char[] caracteres = message.toCharArray();

for (int i = 0; i < caracteres.length; i++) {

char c = caracteres[i];

c++;

caracteres[i] = c;

141

caracteres[i] = c;

}

return new String(caracteres);

}

public String decrypter(String message) {

char[] caracteres = message.toCharArray();

for (int i = 0; i < caracteres.length; i++) {

char c = caracteres[i];

c--;

caracteres[i] = c;

}

return new String(caracteres);

}

}

Stratégie - Exemple/**

* Masque XOR 103 (ConcreteStrategy)

*/

public class Mask implements IStrategy {

public String encrypter(String message) {

char[] caracteres = message.toCharArray();

for (int i = 0; i < caracteres.length; i++) {

int c = caracteres[i];

c = c ^ 103;

caracteres[i] = (char)c;

}

142

}

return new String(caracteres);

}

public String decrypter(String message) {

char[] caracteres = message.toCharArray();

for (int i = 0; i < caracteres.length; i++) {

int c = caracteres[i];

c = c ^ 103;

caracteres[i] = (char)c;

}

return new String(caracteres);

}

}

Stratégie - Exemple/**

* Message secret (Context)

*/

public class Message {

private IStrategy strategie;

private String message;

public Message() {

this.message = "";

this.strategie = null;

}

143

}

public String getMessage() {

return this.message;

}

public void setMessage(String msg) {

this.message = msg;

}

// ...

Stratégie - Exemple

// ...

public void setStrategie(IStrategy s) {

this.strategie = s;

}

public void encrypter() {

this.message = this.strategie.encrypter(this.message);

}

144

}

public void decrypter() {

this.message = this.strategie.decrypter(this.message);

}

}

Stratégie - Exemplepublic class Main {

public static void main(String[] args) {String message = "Bonjour le monde!"; Message msg = new Message();

System.out.println("--- Caesar ---");

msg.setMessage(message);msg.setStrategie(new Caesar());

System.out.println("Message original : " + message);

145

System.out.println("Message original : " + message);msg.encrypter();System.out.println("Message crypté : " + msg.getMessage());msg.decrypter();System.out.println("Message décrypté : " + msg.getMessage());

System.out.println("--- Mask ---");

msg.setMessage(message);msg.setStrategie(new Mask());

System.out.println("Message original : " + message);msg.encrypter();System.out.println("Message crypté : " + msg.getMessage());msg.decrypter();System.out.println("Message décrypté : " + msg.getMessage());

}}

Template Method

• Définir le squelette d’un algorithme et laisser les sous-classes implanter les détails des étapes

• Problème

– Un processus fait intervenir un algorithme dont – Un processus fait intervenir un algorithme dont certaines étapes peuvent être implantées de différentes façons

– On veut fixer certaines parties de l’algorithme et faire varier d’autres parties

Friday, February 05, 2010 Luc Lamontagne 146

Template Method

• Solution– Définir les étapes de l’algorithme dans une classe parent

• Concrète : Implantation par défaut des étapes• Abstraite : Pas d’implantation par défaut des étapes

– Une méthode de la classe parent contrôle la séquence d’exécution des étapes (Template Method)

– Une méthode de la classe parent contrôle la séquence d’exécution des étapes (Template Method)

• Cette méthode ne peut être redéfinie par les classes enfant (final)

– La classe parent implante les étapes invariables• Peut également implanter une logique par défaut des étapes variables

– Les classes enfant implantent les étapes variables• Reporter les détails des étapes dans les sous-classes

Friday, February 05, 2010 Luc Lamontagne 147

Template Method

148

Template Method

• Conséquences

– Permet de définir le squelette d’un algorithme

– Transfert le contrôle de la séquence d’exécution de l’algorithme à la classe parentde l’algorithme à la classe parent

Friday, February 05, 2010 Luc Lamontagne 149

Template Method - Exemple

• Algorithme de validation de cartes de crédit

Friday, February 05, 2010 Luc Lamontagne 150

Template Method - Exemple

• Algorithme de validation de cartes de crédit

– La validation d’une carte de crédit se fait en 4 étapes

• Vérification du nom du détenteur

• Vérification du préfix du numéro de la carte

• Vérification du numéro de la carte

• Vérification de la date d’expiration

– La même séquence est valable pour tout les types de cartes de crédit

– Les méthodes et les conditions de validation diffèrent selon le type de carte

Friday, February 05, 2010 Luc Lamontagne 151

Template method - Exemple

• Validation d’une carte de crédit

Friday, February 05, 2010 Luc Lamontagne 152

Template Method - Exemple/**

* Asbtract Class

*/

public abstract class CarteCredit {

private String numero;

private String nom;

private int expiration;

public CarteCredit(String nom, String numero, int expiration) {

this.nom = nom;

this.numero = numero;

153

this.expiration = expiration;

}

public String getNom() {

return this.nom;

}

public String getNumero() {

return this.numero;

}

public int getExpiration() {

return this.expiration;

}

// ...

Template Method - Exemple

// ...

// Template Method

public final boolean estValide() {

if (nomValide() && prefixValide() && numeroValide() && expirationValide()) {

return true;

} else {

return false;

}

}

154

}

public abstract boolean nomValide();

public abstract boolean prefixValide();

public abstract boolean numeroValide();

public abstract boolean expirationValide();

}

Template Method - Exemple

/**

* Carte de crédit de type VISA (ConcreteClass)

*/

public class CarteVisa extends CarteCredit {

public CarteVisa(String nom, String numero, int expiration) {

super(nom, numero, expiration);

}

public boolean nomValide() {

155

public boolean nomValide() {

// Vérification du détenteur de la carte

System.out.println("VISA : Vérification nom");

return true;

}

// ...

Template Method - Exemple

// ...

public boolean prefixValide() {System.out.println("VISA : Vérification préfix");if (getNumero().startsWith(“450")) {

return true;} else {

return false;}

}

156

public boolean expirationValide() {System.out.println("VISA : Vérification date expiration");if (getExpiration() > 1001) {

return true;} else {

return false;}

}

// ...

Template Method - Exemple// ...

public boolean numeroValide() {System.out.println("VISA : Vérification numéro");if (getNumero().length() == 13) {

int somme = 0;String digit;for (int i = 0; i < getNumero().length(); i++) {

try {digit = getNumero().substring(i, i+1);somme += Integer.parseInt(digit);

} catch (Exception e) {somme += 0;

}

157

} }

if (somme == 50) {return true;

} else {return false;

}} else {

return false;}

}}

Template Method - Exemple

public class Main {

public static void main(String[] args) {

CarteCredit visa = new CarteVisa("Robert Laberge", "1234532896043", 2009);

if (visa.estValide()) {

System.out.println("Carte VISA appartennant à " + visa.getNom() + " est valide");

} else {

158

System.out.println("Carte VISA appartennant à " + visa.getNom() + " estinvalide");

}

}

}

Objet null

• Null en programmation– Objet non-existant (i.e pas d’objet)– On ne peut pas invoquer de méthodes– On doit vérifier avant de l’utiliser

• Objet Null– Objet de la classe “non-existante”– Utile si une sous-classes n’est pas disponible– On lui associe un comportement par défaut

• Offre les mêmes méthodes que les autres sous-classes

– Pas de vérification et permet un traitement uniforme

Friday, February 05, 2010 Luc Lamontagne 159

Objet null

• File logging

Friday, February 05, 2010 Luc Lamontagne 160

Objet null

public class NullLogger implements Logger {

public void log(String msg) { }

}

public class LoggerTest {public class LoggerTest {

public static void main(String[] args) {

LoggerFactory factory = new LoggerFactory();

Logger logger = factory.getLogger();

logger.log("A Message to Log");

}

}

Friday, February 05, 2010 Luc Lamontagne 161

La suite…

• Étude des styles architecturaux

– Styles de bases

– Interactionnel

• Maîtrise des principales notions de Java• Maîtrise des principales notions de Java

– Adaptatif

• Utilisé pour décrire les structures

– ...

Friday, February 05, 2010 Luc Lamontagne 162

Recommended