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