DesignPatterns comportement H10 - Université Lavallamontagne/glo3001/DesignPatterns...Patrons de...
Transcript of DesignPatterns comportement H10 - Université Lavallamontagne/glo3001/DesignPatterns...Patrons de...
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("[email protected]", "spam: ceci est un spam !!!");
chaine.request("[email protected]", "Message de l'Université Laval");
36
chaine.request("[email protected]", "Message de l'Université Laval");
chaine.request("[email protected]", "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