Design Patterns
Transcription
Design Patterns
Design Patterns Design Patterns Vie d’un source... Design Patterns The Liskov Substitution Principle (LSP) 1. joli, pur, “beau” les dégradations du design sont liées aux modifications des spécif 2. une première “héresie” ces modifications sont “à faire vite” et par d’autres que les designers originaux ⇒ “ça marche” mais ss respect du design initial ⇒ corruption du design (avec effet amplifié au fur et à mesure) 3. de plus en plus d’horreurs 4. toujours plus d’horreurs Les sous-classes doivent pouvoir remplacer leur classe de base Les méthodes qui utilisent des objets d’une classe doivent pouvoir utiliser “inconsciemment” des objets dérivés de cette classe 5. des horreurs partout mais les modifs sont inévitables la conception/design doit permettre ces modifications (les amortir : “firewalls”) 1. de moins en moins maintenable et évolutif On doit pouvoir Upcaster sémantiquement les dégradations sont dues aux dépendances et à l’architecture des dépendances 2. design submergé par les “horreurs” USTL http://www.lifl.fr/∼routier 2 USTL 4 http://www.lifl.fr/∼routier Design Patterns USTL 6 http://www.lifl.fr/∼routier Design Patterns Design Patterns Principes et Patterns Symptômes Rigidité difficulté à changer (même légèrement) (effet domino) pb pour corriger des bugs non critiques Fragilité corruption à la moindre modification ֒→ corruption dans des zones qui n’ont rien à voir avec modif une correction ⇒ plus de pb perte de contrôle sur le code organisation des dépendances inter-classes Immobilité pas réutilisabilité inter-projets ֒→ l’effort et le travailour extraitre les fonctionnalités récupérables d’un projet est supérieur au coût de redéveloppement (et au risque) Design Patterns (déjà évoqués : Open Close Principle, Liskov Substitution Principle) abstraction des problèmes traités Viscosité (opacité) design qd pour une modif il existe plusieurs modifications : design propre (difficile) 6= hack (facile) environnement qd l’environnement est lent ֒→ incitation à faire ce qui évite l’utilisation de l’environnement (au mépris du design). USTL http://www.lifl.fr/∼routier 1 USTL http://www.lifl.fr/∼routier 3 USTL http://www.lifl.fr/∼routier 5 Design Patterns Design Patterns Dependency Inversion Principle (DIP) Design Patterns Interface Segregation Principle (ISP) Plusieurs interfaces client spécifiques valent mieux qu’une seule interface générale les clients ne doivent pas être forcés de dépendre d’interfaces qu’ils n’utilisent pas Eviter : Inverser les dépendances : les modules de bas niveau doivent se conformer à des interfaces définies (car utilisées) par les modules de haut niveau ou Inversion of Control qu’un client voit une interface qui ne le concerne pas que les évolutions dues à une partie du service aient un impact sur un client alors qu’il n’est pas concerné Dépendez des abstractions. Ne dépendez pas des concrétisations. 1. Les modules de haut niveau ne doivent pas dépendre de modules de bas niveau. Tous deux doivent dépendre d’abstractions Dépendre d’interfaces et de classes abstraites plutôt que de classes. 2. Les abstractions ne doivent pas dépendre de détails. Les détails doivent dépendre d’abstractions. utiliser l’abstraction (encore et toujours... ) Frameworks USTL 8 http://www.lifl.fr/∼routier USTL 10 http://www.lifl.fr/∼routier Design Patterns USTL Design Patterns Généralement : réutilisation des modules métiers (de haut niveau) pas possible ils sont construits sur les modules de bas niveau directement (IHM, comm., etc.) Le dilemme cercle/ellipse Design Patterns Exemple : dépendance personnages/choses du projet Nourriture un Cercle est une Ellipse Ellipse : 2 foyers << interface >> Heros Item Heros Circle : 1 seul centre + utilise(Nourriture) + utilise(Richesse) + estUtilisePar(Heros) + utilise (Item) Richesse problème avec setFoyer() Richesse 7 USTL http://www.lifl.fr/∼routier Application 9 USTL Item Personnage + utilise(Item) modification nécessaire des modules de haut niveau lorsque les bas niveaux sont modifiés réutilisation des modules de haut niveau indépendamment des bas niveaux impossible =⇒ pas de réutilisation de la logique de l’application en dehors du contexte technique ! (ce n’est pas le cas Cercle pour Ellipse, dans ce cas utiliser la composition) http://www.lifl.fr/∼routier Framework Heros Nourriture << interface >> << interface >> Problèmes ne pas utiliser l’héritage juste pour factoriser du code héritage =⇒ extension/spécialisation USTL 12 http://www.lifl.fr/∼routier + estUtilisePar(Personnage) Richesse http://www.lifl.fr/∼routier Nourriture 11 Design Patterns Design Patterns Design Patterns Comment reconnaı̂tre ces situations ? Patrons de conception Structure Une représentation graphique des classes dans le pattern utilisant une notation basée sur l’Object Modeling Technique (OMT). Participants Les classes et/ou les objets qui participent au design pattern et leurs responsabilités. D ESIGN PATTERNS Elements of reusable objected-oriented softwares Collaborations Comment les participants collaborent pour tenir leurs responsabilités. Addison Wesley Consequences Gang of Four : E. Gamma, R. Helm, R. Johson, J. Vlissides Comment le pattern satisfait-il ses objectifs ? Quelles sont les conséquences et resultats de l’usage du pattern ? Quels points de la structure vous laisse-t-il faire varier indépendamment ? (+ dans Model : setState()) “Design Patterns. Tête la première.” Eric et Elisabeth Freeman Implementation Quels pièges, astuces ou techniques devriez vous connaı̂tre pour implémenter ce pattern ? Y a-t-il des problèmes spécifiques à un langage ? Applicable à d’autres domaines que IHM USTL 14 http://www.lifl.fr/∼routier USTL 16 http://www.lifl.fr/∼routier Design Patterns USTL Design Patterns Design Patterns Description d’un pattern L’ancêtre ? MVC Solution = Séparation 18 http://www.lifl.fr/∼routier Pattern Name and Classification Le nom du pattern, évoque succinctement l’esprit du pattern. Un pattern pour les IHM : découplage IHM/applicatif. s’appuie sur le pattern Observer/Observable. Intent Une brève description qui répond aux questions suivantes : Plusieurs vues possibles pour une “même donnée” =⇒ gérer la cohérence. Qu’est ce que le design pattern fait ? Quels sont son objectif et sa justification ? Quel problème de conception particulier vise-t-il ? Also Known As Autres noms connus pour le pattern, si il y en a. Motivation Un scénario qui illustre un problème de conception et comment la structuration des classes et des objets dans ce pattern le résolvent. Le scénario devra aider à comprendre la description plus abstraite du pattern qui suit. Applicability multi-héritage (upcast) (JAVA via interfaces) USTL Quelles sont les situations dans lesquelles le pattern peut être appliqué ? Quels sont les exemples de mauvaise conception que le pattern peut attaquer ? utilisation du design pattern Adapter Souvent au niveau IHM, vue et contrôleur se confondent. http://www.lifl.fr/∼routier 13 USTL http://www.lifl.fr/∼routier 15 USTL http://www.lifl.fr/∼routier 17 Design Patterns Creational Patterns Abstraction du processus d’instanciation. Permettent de rendre le système indépendant du mode de création des objets qui le compose. Un creational pattern de classe utilise l’héritage pour faire varier la classe instanciée. Un creational pattern d’objets delègue la création à un autre objet. Structural Patterns Comment composer classes et objets pour obtenir des structures plus complexes. Un structural pattern de classe utilise l’héritage pour composer des interfaces ou des implémentations. Un structural pattern objet décrit comment composer des objets pour obtenir de nouvelles fonctionnalités, avec possibilité de faire évoluer la composition à l’exécution. State Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. Bridge Decouple an abstraction from its implementation so that the two can vary independently. Strategy Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. Template Method Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure. Visitor Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates. Facade Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use. Flyweight Use sharing to support large numbers of fine-grained objects efficiently. Proxy Provide a surrogate or placeholder for another object to control access to it. 20 http://www.lifl.fr/∼routier Adapter Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces. Decorator Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. Décrivent également les patterns de communication entre classes et objets : permettent donc de se dégager du problème du flots de contrôle et de se concentrer sur les relations entre objets. Un behavioral pattern de classe utilise l’héritage pour distribuer les comportements entre les classes. Un behavioral pattern objet utilise l’héritage plutôt que la composition. Certains décrivent les coopérations entre objets, d’autres utilisent l’encapsulation du comportement dans un objet auquel sont déléguées les requêtes. Design Patterns Observer Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. Composite Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly. Behavioral Patterns Concernent les algorithmes et la répartitions des reponsabilités entre les objets. USTL Design Patterns Structural Patterns USTL 22 http://www.lifl.fr/∼routier Design Patterns Chain of Responsibility Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it. Command Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations. Builder Separate the construction of a complex object from its representation so that the same construction process can create different representations. Known Uses Exemples d’utilisation du pattern trouvés dans des systèmes réels. Interpreter Given a language, define a represention for its grammar along with an interpreter that uses the representation to interpret sentences in the language. Factory Method Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. Quels design patterns sont fortement liés à celui-ci et quelles sont les différences importantes ? Avec quels autres patterns celui-ci devrait il être utilisé ? Design Patterns Behavioral Patterns Abstract Factory Provide an interface for creating families of related or dependent objects without specifying their concrete classes. Related Patterns 24 http://www.lifl.fr/∼routier Design Patterns Creational Patterns Sample Code Portions de code qui illustrent comment vous pourriez implémenter ce pattern. USTL Iterator Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation. Prototype Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype. Mediator Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently. Singleton Ensure a class only has one instance, and provide a global point of access to it. Memento Without violating encapsulation, capture and externalize an object’s internal state so that the object can be restored to this state later. USTL http://www.lifl.fr/∼routier 19 USTL http://www.lifl.fr/∼routier 21 USTL http://www.lifl.fr/∼routier 23 Design Patterns Design Pattern, creational : “Singleton” Design Patterns Design Patterns Design Pattern, Structural : “Composite” Design Pattern, behavioural : “Iterator” Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly. Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation. Ensure a class only has one instance, and provide a global point of access to it. Rendre privé le constructeur, Construire une instance de la classe comme “dans” la classe, Fournir une méthode d’accès à cette instance USTL 26 http://www.lifl.fr/∼routier USTL 28 http://www.lifl.fr/∼routier Design Patterns USTL http://www.lifl.fr/∼routier Design Patterns Design Patterns En JAVA Pattern et Idiom 30 composants AWT / SWING et leur méthode repaint() public class SingletonClass { private SingletonClass() { } private SingletonClass uniqueInstance = new SingletonClass(); public static SingletonClass getTheInstance() { return uniqueInstance; } } ou Pattern Solution générique à un problème (générique) de design. Solution éprouvée à une classe de problèmes “classique” ֒→ abstraction renforcée separate things that change from things that stay the same public class SingletonClass { private SingletonClass() { } public static final SingletonClass uniqueInstance = new SingletonClass(); } Idiom Solution spécifique dans un langage donné pour un problème de design (éventuellement propre au langage). Exemple : gestion des types de terrains dans le projet 1. Usage un peu détourné : faible nombre d’instances : implémentation des types énumérés en JAVA USTL http://www.lifl.fr/∼routier 25 USTL http://www.lifl.fr/∼routier 27 USTL http://www.lifl.fr/∼routier 29 Design Patterns Exemples de “factory method” class Circle extends Shape { Circle() {} // Friendly constructor public void draw() { System.out.println("Circle.draw"); } public void erase() { System.out.println("Circle.erase"); } } OCP ? 32 http://www.lifl.fr/∼routier public interface LivingFactory { public Living createLiving(); } public class TheFactories { public static Map factories = new HashMap(); public static final Living createLiving() {// "throw UnknownMonstreException" return ((LivingFactory) allFactories.get(living)).createLiving(); } public class MonstrePasBô extends Living { static { TheFactories.factories.put("MonstrePasBô", new MonstrePasBôFactory()); } (...) } public class MonstrePasBôFactory implements LivingFactory { public Living createLiving() { return new MonstrePasBô(); } } public class MonstrePasGentil extends Living { static { TheFactories.factories.put("MonstrePasGentil", new MonstrePasGFactory()); } (...) } public class MonstrePasGFactory implements LivingFactory { public Living createLiving() { return new MonstrePasGentil(); } } USTL L’ajout de nouvelles classes se fait grâce au polymorphisme ⇒ modifications au niveau des méthodes héritées MAIS il reste à créer les instances, ⇒ effets aux endroits où sont créés les objets une classe ne peut pas anticiper la classe des objets qu’elle doit créer exemples : méthode iterator() dans Collection http://www.lifl.fr/∼routier 31 USTL 36 Design Patterns éviter la “pollution des classes” : utiliser des classe internes public class MonstrePasBô extends Living { static { TheFactories.factories.put("MonstrePasBo", new MonstrePasBô.MaFactory()); } (...) class MaFactory implements LivingFactory { // classe interne public Living createLiving() { return new MonstrePasBô(); } } } public class Heros extends Living { static { TheFactories.factories.put("Heros", new Heros.MaFactory()); } class MaFactory implements Factory { public Living createLiving() { return new Heros(); } } (...) } public class Shark extends Fish { (...) public Fish createNewFish(Position lastPosition) { return new Shark(lastPosition, gestationPeriod, starvingPeriod); } }// Shark une classe veut que ses sous-classes spécifient les objets qu’elle crée. http://www.lifl.fr/∼routier //création à partir du nom de classe Living living = TheFactories.createLiving("MonstrePasBô"); ... int alea = new random.nextInt(TheFactories.factories.size()); Iterator it = TheFactories.factories.keySet().iterator(); // création "aléatoire" for (int i = 0 ; i < alea; i++) { it.next(); } living = TheFactories.createLiving((String) it.next()); public class Tuna extends Fish { (...) public Fish createNewFish(Position lastPosition) { return new Tuna(lastPosition, gestationPeriod); } }// Tuna À utiliser quand : USTL Usage public abstract class Fish { (...) public Fish giveBirth(Position lastPosition) { if (gestationPeriod <= timeSinceLastBirth++) { timeSinceLastBirth = 0; return createNewFish(lastPosition); } else { return null; } } public abstract Fish createNewFish(Position lastPosition); }// Fish “virtual constructor” Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations. Design Patterns Gérer par les sous-classes Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. USTL 34 http://www.lifl.fr/∼routier Design Patterns Design Pattern, creational : “Factory Method” Design Patterns Design Pattern, behavioural : “Command” Exemple abstract class Shape { public abstract void draw(); public abstract void erase(); public static Shape factory(String type) throws BadShapeCreation { if(type == "Circle") return new Circle(); if(type == "Square") return new Square(); throw new BadShapeCreation(type); } } USTL Design Patterns http://www.lifl.fr/∼routier 33 USTL http://www.lifl.fr/∼routier 35 Design Patterns Design Pattern, structural : “Decorator” Design Patterns Design Patterns Design Pattern, behavioural : “Strategy” public interface Strategie { public Coup calculDuCoupAJouer(...); // classe Coup définie "ailleurs" Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. } Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. public class Joueur { private Strategie maStrategie; public Joueur(Strategie strategie, ....) { maStrategie = strategie; ... } public Coup joue() { return maStrategie.calculDuCoupAJouer(...); } } public class StrategieAleatoire implements Strategie { public Coup calculDuCoupAJouer(...) // { ... choix }; aléatoire } public class StrategieImpl implements Strategie { public Coup calculDuCoupAJouer(...) // { ... un autre }; calcul } Counter USTL http://www.lifl.fr/∼routier 38 et IncrementFunction USTL ou http://www.lifl.fr/∼routier Design Patterns Joueur // ... utilisation Joueur joueur1 = new Joueur(new StrategieAleatoire()); Joueur joueur2 = new Joueur(new StrategieImpl()); new Jeu(joueur1, joueur2).unTourDeJeu(); et Strategie 40 USTL Design Patterns Design Patterns Application : gestion du menu joueur dans le projet 1 public interface IncrementFunction { public int increment(int value); } public interface Command { public boolean execute(Player player); public class SimpleIncrement implements IncrementFunction { ... } public class ModularIncrement implements IncrementFunction { ... } public class AnotherIncrement implements IncrementFunction { ... } } public class Dialog { private HashMap commands = new HashMap(); private Player player; public Dialog (Player player){ this.player = player; init(); } private void init() { commands.put("look",new LookCommand(player)); commands.put("move",new MoveCommand(player)); (...) } public void act() { // très allégé ici System.out.println("what do you want to do (typing ‘help’ can use) ?"); command = Input.readString(); done = ((Command) commands.get(command)).execute(player); } } public class LookCommand implements Command { public void execute(Player player) { (...) } } public class MoveCommand implements Command { public void execute(Player player) { (...) } } USTL http://www.lifl.fr/∼routier 42 http://www.lifl.fr/∼routier 37 public class Counter { private int value; private IncrementFunction incrementF; public Counter(int value, IncrementFunction incrementF) { this.value = value; his.incrementF = incrementF; } public int getCurrentValue() { return value: } public void increment() { value = incrementF.increment(value); } public void initValue(int init) { this.value = init; } } // ... exemple : BufferedReader USTL utilisation Counter simpleCounter = new Counter(0, new SimpleIncrement()); Counter modularCounter = new Counter(0, new ModularIncrement(7)); Counter anotherCounter = new Counter(0, new AnotherIncrement()); http://www.lifl.fr/∼routier 39 USTL http://www.lifl.fr/∼routier 41 Design Patterns Design Pattern, behavioural : “Oberver/Observable” Design Patterns Les contraintes Design Patterns Étape 1 Plusieurs objets peuvent être avertis des événements émis par une source (d’après B. Venners) Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. Le nombre et la nature des objets avertis ne sont potentiellement pas connus à la compilation et peuvent changer dans le temps. Définir les classes d’événements L’émetteur de l’événement et le récepteur ne sont pas fortement liés. Définir une classe par type d’événements qui peuvent être émis par le générateur d’événements Faire hériter ces classes de java.util.EventObject Concevoir les classes d’événements de telle sorte qu’un événement englobe toute l’information qui doit être transmises à l’observateur Donner à une classe d’évenement un nom de la form XXXEvent mettre en place un mécanisme de délégation entre l’émetteur de l’événement (event generator) et le récepteur (listener) Exemple Un téléphone sonne, différentes personnes ou machines peuvent potentiellement être concernées par l’appel. Les personnes peuvent sortir ou entrer dans la pièce et ainsi sortir ou entrer du “champ d’intéressement” du téléphone. Observer/Observable pattern ou Abonneur/Abonné pattern ou “event generator” idiom USTL 44 http://www.lifl.fr/∼routier USTL Design Patterns Design Pattern, behavioural : “Template method” 46 http://www.lifl.fr/∼routier http://www.lifl.fr/∼routier Design Patterns Problème et exemple Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure. USTL 48 Design Patterns Les étapes de design JAVA problème : un objet (acteur) doit réagir lorsqu’un autre objet remarque un événement, mais ce dernier n’a pas nécessairement conscience de l’existence de l’acteur (en fait des acteurs) 1. Définir les classes d’événements Les (ré)acteurs peuvent : 2. Définir les interfaces des listeners être de différentes natures 3. Définir les classes des adapters (optionnel) changer sans que l’émetteur de l’événement en soit conscient 4. Définir la classe émétrice (génératrice des événements) 5. Définir les classes réceptrices (les listeners) exemples la gestion des événements dans awt/Swing détection de fin de jeu dans le projet 1 Exemple : algorithme MinMAX USTL http://www.lifl.fr/∼routier 43 USTL http://www.lifl.fr/∼routier 45 USTL http://www.lifl.fr/∼routier 47 Design Patterns Étape 2 Design Patterns Étape 4 Design Patterns Étape 5 Définir les interfaces des listeners pour chaque type d’événements, définir une interface qui hérite de java.util.EventListener et contient, pour chaque événement, une déclaration de méthode qui sera déclenchée lors de la notification de l’occurrence d’un événement Le nom de cette interface est obtenu à partir du nom de la classe de l’événement en remplaçant Event par Listener Les noms des méthodes de l’interface sont construits à partir d’un verbe au passé indiquant la situation d’activation. Chaque méthode retourne void et prend un paramètre qui est de la classe de l’événement Définir la classe émétrice (génératrice des événements) Pour chaque type d’événements émis, définir un couple de méthodes add/remove Le nom de ces méthodes est de la forme : addlistener-interface-name() et removelistener-interface-name(). Méthode d’abonnement, désabonnement Pour chacune des méthodes des interfaces des listeners, définir une méthode private de propagation de l’événement. Cette méthode ne prend pas de paramètre et retourne void. Cette méthode est nommée : firelistener-method-name. Déclencher les événements. package essais.observer; public interface TelephoneListener extends java.util.EventListener { public void telephoneRang(TelephoneEvent e); public void telephoneAnswered(TelephoneEvent e); } USTL 50 http://www.lifl.fr/∼routier USTL Les classes qui veulent être des listeners n’ont qu’à implémenter l’interface listener associée 52 http://www.lifl.fr/∼routier Design Patterns Définir les classes réceptrices (les listeners) Définir les classes des adapters (optionnel) Pour chaque listener on définit une classe qui l’implémente en définissant des méthodes creuses La classe de l’adapter est obtenue en remplaçant Listener par Adapter. package essais.observer; public class TelephoneAdapter implements TelephoneListener { public void telephoneRang(TelephoneEvent e) { } public void telephoneAnswered(TelephoneEvent e) { } } 2 modèles dans le design pattern “observer” : pull-model (l’observateur doit extraire les infos de la source) et push-model (toute l’information est encapsulée directement dans l’événement) http://www.lifl.fr/∼routier 49 USTL http://www.lifl.fr/∼routier 54 Design Patterns package essais.observer; import java.util.*; public class Telephone { private ArrayList telephoneListeners = new ArrayList(); public void ringPhone() { fireTelephoneRang(); } public void answerPhone() { fireTelephoneAnswered(); } public synchronized void addTelephoneListener(TelephoneListener l) { if (telephoneListeners.contains(l)) { return ; } telephoneListeners.add(l); } public synchronized void removeTelephoneListener(TelephoneListener l){ telephoneListeners.remove(l); } private void fireTelephoneRang() { ArrayList tl = (ArrayList) telephoneListeners.clone(); if (tl.size() == 0) { return; } TelephoneEvent event = new TelephoneEvent(this); for (Iterator it = tl.iterator();it.hasNext();) { ((TelephoneListener) it.next()).telephoneRang(event); } } private void fireTelephoneAnswered() { ... } } EventObject.getSource() : disposer de la source permet d’être abonné à plusieurs sources pour le même type d’évenements USTL http://www.lifl.fr/∼routier Design Patterns Étape 3 package essais.observer; public class TelephoneEvent extends java.util.EventObject { public TelephoneEvent(Telephone source) { super(source); } } USTL 51 USTL http://www.lifl.fr/∼routier 53 Design Patterns Design Patterns Behavioral pattern : Visitor package essais.observer; Design Patterns Autres modifications de traitements (from J. Blosser) public class Example1 { public static void main(String[] args) { “Séparer la structure d’une collection des traitements effectués sur la collection” public void messyPrintCollection(Collection collection) { Iterator iterator = collection.iterator() while (iterator.hasNext()) { Object o = iterator.next(); if (o instanceof Collection) messyPrintCollection((Collection)o); else if (o instanceof String) System.out.println("’"+o.toString()+"’"); else if (o instanceof Float) System.out.println(o.toString()+"f"); else System.out.println(o.toString()); } Telephone ph = new Telephone(); Person bob = new Person(); AnsweringMachine am = new AnsweringMachine(); JAVA : utilisation des iterator ? ph.addTelephoneListener(am); bob.listenToPhone(ph); Problème : modification des types des objets contenus dans la collection... ph.ringPhone(); ph.answerPhone(); } } public void messyPrintCollection(Collection collection) { Iterator iterator = collection.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next().toString()); } } USTL 56 http://www.lifl.fr/∼routier USTL 58 http://www.lifl.fr/∼routier Design Patterns Open Close Principle ?!? USTL 60 http://www.lifl.fr/∼routier Design Patterns Design Patterns Introduction du traitement de collections de collections... Guidelines d’implémentation passer la même instance à tous les listeners pour un événement donné package essais.observer; public class AnsweringMachine implements TelephoneListener { public void telephoneRang(TelephoneEvent e) { System.out.println("AM hears the phone ringing."); } public void telephoneAnswered(TelephoneEvent e) { System.out.println("AM sees that the phone was answered."); } } rendre l’objet événement immuable utiliser un seul thread pour avertir tous les listeners public void messyPrintCollection(Collection collection) { Iterator iterator = collection.iterator() while (iterator.hasNext()) { Object o = iterator.next(); if (o instanceof Collection) messyPrintCollection((Collection)o); else System.out.println(o.toString()); } } créer une image de la liste des listeners abonnés avant de les avertir les méthodes des listeners doivent être éxécutées rapidement (car notifiés 1 par 1) package essais.observer; class MyTelephoneAdapter extends TelephoneAdapter { public void telephoneRang(TelephoneEvent e) { System.out.println("I’ll get it!"); } } public class Person { public void listenToPhone(Telephone t) { t.addTelephoneListener(new MyTelephoneAdapter()); } } l’ordre des notification ne doit pas être important créer des adpaters pour permettre aux listeners de se concentrer que sur certains événements. différences de fréquences entre l’occurrence de deux évenements émis par une même source : créer des listeners différents cf. MouseListener et MouseMotionListener java.util.Observer et java.util.Observable USTL http://www.lifl.fr/∼routier 55 USTL http://www.lifl.fr/∼routier 57 USTL http://www.lifl.fr/∼routier 59 Design Patterns et le visiteur qui va avec... Design Patterns ReflectiveVisitor public class PrintVisitor implements Visitor { public void visitCollection(Collection collection) { Iterator iterator = collection.iterator() while (iterator.hasNext()) { Object o = iterator.next(); if (o instanceof Visitable) ((Visitable)o).accept(this); } protected Method getMethod(Class c) { Class newc = c; Method m = null; while (m == null && newc != Object.class) { // Try the superclasses try { m = getClass().getMethod("visit", new Class[] { newc }); } catch (NoSuchMethodException e) { newc = newc.getSuperclass(); } } if (newc == Object.class) { // Try the interfaces. Class[] interfaces = c.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { try { m = getClass().getMethod("visit", new Class[] { interfaces[i] }); } catch (NoSuchMethodException e) {} } } if (m == null) { try { m = thisclass.getMethod("defaultVisit", new Class[] { Object.class }); } catch (Exception e) { } // Can’t happen } return m; } public interface ReflectiveVisitor { public void visit(Object o); } public class PrintVisitor implements ReflectiveVisitor { public void visit(Collection collection) { idem } public void visit(String string) { idem } public void visit(Float float){ idem } public void defaultVisit(Object o) { S.o.p(o.toString()); } public void visitString(String string) { System.out.println("’"+string+"’"); public void visit(Object o) { try { Method m = getClass().getMethod("visit", new Class[] { o.getClass() }); m.invoke(this, new Object[] { o }); } catch (NoSuchMethodException e) { this.defaultVisit(o); } } } public void visitFloat(Float float) { System.out.println(float.toString()+"f"); } } } “double dispatch” : Visitor→Visitable→Visitor USTL 62 http://www.lifl.fr/∼routier USTL 64 http://www.lifl.fr/∼routier Design Patterns USTL public interface Visitor { public void visitCollection(Collection collection); public void visitString(String string); public void visitFloat(Float float); } 66 http://www.lifl.fr/∼routier Design Patterns Bof ! Design Pattern Visitor à la rescousse Design Patterns une “getMethod” adaptée Design Patterns Bénéfices ? plus de if-then-else mais... beaucoup de code + “enrobage” des objets pour l’interface Visitable public interface Visitable { public void accept(Visitor visitor); } Plus besoin de classes Visitable d’enrobage public class VisitableString implements Visitable { private String value; public VisitableString(String string) { value = string; } public void accept(Visitor visitor) { visitor.visitString(this.value); } } Possibilité de gérer les cas non pris en compte (directement) par capture d’exception : essayer les superclasses et interfaces (pour les arguments)... de plus : ajout de VisitableInteger⇒ changement de l’interface Visitor !! Solution ? utilisation de la réflexivité + VisitableFloat et VisitableCollection... USTL http://www.lifl.fr/∼routier 61 USTL http://www.lifl.fr/∼routier 63 USTL http://www.lifl.fr/∼routier 65 Design Patterns visit devient... public void visit(Object o) { try { Method m = getMethod(o.getClass()}); m.invoke(this, new Object[] { o }); } catch (NoSuchMethodException e) { } } Il est possible de garder l’interface Visitable pour permettre aux objets visités de contrôler la navigation si on le désire. pour la même collection, on peut facilement avoir différents Visitors USTL http://www.lifl.fr/∼routier 67