|
|
Rappels :
Vous devez fournir des copies d'écran des diagrammes de classe de BlueJ,
et la documentation générée doit faire apparaître
vos commentaires sur toutes les classes, les méthodes, les paramètres,
et les valeurs de retour.
Attention !
Les questions signalées par *** doivent être lues pour la compréhension de la suite du sujet, mais sont optionnelles, c'est-à-dire peuvent ne pas être rendues pour gagner du temps. |
Question 1. (Test du pattern Observateur/Observé)
(i) Le paquetage java.util propose la classe Observable et l'interface Observer (lire leur javadoc).
On souhaite vérifier le fonctionnement de ces entités Java qui implémentent le pattern Observateur/Observé vu en cours.
Pour cela, il faut concevoir une classe de test qui vérifie
notamment que lors d'une notification, TOUS les observateurs ont été
informés, que les arguments ont bien été
transmis, et que le notifiant est correct.
Le projet BlueJ aura la particularité de n'être
constitué que d'une classe de test.
Un point de départ pour cette classe de test est donné à l'annexe 1.
(ii) Ajoutez des méthodes de test
(i) On souhaite maintenant mettre en oeuvre le pattern Observateur dans une petite application permettant d'observer le comportement d'un entier et d'un réel.
Notamment, les observateurs doivent être prévenus lors de
chaque changement d'état d'un nombre.
Prendre connaissance des classes suivantes :
// NombreObservable.java public abstract class NombreObservable extends java.util.Observable { abstract public double valeur(); abstract public void inc(); } // NombreObservable
// Entier.java public class Entier extends NombreObservable { private int entier; public Entier( int e ) { entier = e; } public double valeur() { return (double)entier; } public void inc() { entier++; /* à compléter */ } } // Entier
// ObservateurDEntier.java public class ObservateurDEntier implements java.util.Observer { private char avant, apres; public ObservateurDEntier( char av, char ap ) { avant = av; apres = ap; } public void update( java.util.Observable obs, Object obj ) { Entier e = (Entier)obs; System.out.println( "ObservateurDEntier : " + avant + (int)e.valeur() + apres ); } // update() } // ObservateurDEntier
// TestObservateur.java public class TestObservateur { public static void main( String[] args ) { Entier e = new Entier( 1 ); // e a 2 observateurs : o1, o2 ObservateurDEntier o1 = new ObservateurDEntier( '(', ')' ); ObservateurDEntier o2 = new ObservateurDEntier( '[', ']' ); e.addObserver( o1 ); e.addObserver( o2 ); Flottant f = new Flottant( 10F ); // f a 1 observateur o3 ObservateurDeFlottant o3 = new ObservateurDeFlottant( '{', '}' ); f.addObserver( o3 ); // un observateur commun ObservateurDeNO o4 = new ObservateurDeNO( '*', '*' ); e.addObserver( o4 ); f.addObserver( o4 ); e.inc(); e.inc(); f.inc(); f.inc(); } // main() } // TestObservateur
AIDE :
Voici le diagramme de classes visé :
|
(ii) Commentez les résultats obtenus suite à l'exécution de la méthode main() de la classe TestObservateur.
(iii) Fournir la documentation regroupant toutes les classes des questions (i) et (ii).
(i) Le paquetage java.awt (Abstract Window Toolkit) permet notamment de gérer les événements engendrés par des boutons dans les applettes en respectant le pattern observateur :
Soit l'applette suivante (voir le source à compléter) :
Le bouton A a 3 observateurs. Le bouton B a un seul observateur (commun avec A).
Générez l'applette avec BlueJ, puis exécutez-la avec
appletviewer sous DOS
pour voir les messages.
(ii) *** Ajouter à cette applette (et au bouton B) la gestion des événements souris.
Vous devriez obtenir le comportement suivant :
Le bouton A a 3 observateurs. Le bouton B a un seul observateur (commun avec A).
L'applette et le bouton B ont un observateur de souris complet.
Générez l'applette avec BlueJ, puis exécutez-la avec
appletviewer pour voir les messages dans la fenêtre DOS.
Pour cela, ajouter dans l'applette la classe ObservateurDeSouris qui doit implémenter l'interface java.awt.event.MouseListener.
AIDE :
Pour respecter l'interface MouseListener, il faut implémenter
toutes les méthodes mouse...ed()
(avec ... = Enter, Exit,
Press, Releas, Click). |
(iii) *** Modifier la classe ObservateurDeSouris pour qu'elle hérite de la classe java.awt.event.MouseAdapter plutôt que d'implémenter l'interface java.awt.event.MouseListener.
Quelles en sont les conséquences pour la classe ObservateurDeSouris ? Ne s'intéresser qu'à l'événement clic.
Vous devriez obtenir le comportement suivant :
Le bouton A a 3 observateurs. Le bouton B a un seul observateur (commun avec A).
L'applette et le bouton B ont un observateur de clic de souris.
Générez l'applette avec BlueJ, puis exécutez-la avec
appletviewer pour voir les messages dans la fenêtre DOS.
AIDE :
Il s'agit du pattern Adapteur qui ici simplifie grandement l'écriture de la classe ObservateurDeSouris lorsque l'on ne s'intéresse pas à tous les événements possibles. |
(iv) Fournir la documentation regroupant toutes les classes de la question (iii).
(i) Soit l'applette suivante :
Cette applette fonctionne selon le principe MVC (Modèle, Vue, Contrôleur) :
Pour cela, la classe Compteur implémente le Modèle (voir le source à compléter) et l'applette IHMCompteur implémente à la fois la Vue et le Contrôleur (voir le source à compléter).
Contraintes :
|
(ii) La Vue et le Contrôleur sont maintenant dans deux classes distinctes afin de mieux refléter le Modèle MVC.
Développer une nouvelle architecture comme suit :
(iii) Fournir la documentation regroupant toutes les classes de la question (ii).
HORS TP ** :
si vous voulez aller plus loin ...
Observateurs "à la main" (i) Soit la situation suivante :
Comprendre et tester les classes suivantes :
(ii) Ajouter les classes nécessaires pour pouvoir créer et observer des flottants. Ajouter dans TestObservateur un flottant, un observateur de ce flottant, et deux incrémentations de ce flottant.
(iii) On désire maintenant ajouter un même observateur pour un entier et un flottant. Ajouter la classe ObservateurDeNO (Nombre Observable) et les tests nécessaires (dans TestObservateur).
(iv) Fournir la documentation regroupant toutes les classes de la question (iii). ** Hors TP signifie "non pris en compte pour l'évaluation". |
import java.util.Observer; import java.util.Observable; import java.util.Stack; // ou mieux : la pile du TP3 public class PatternObservateur extends junit.framework.TestCase { private SujetObservable s1, s2; private Observateur o1, o2; /** Piles pour sauvegarder les emetteurs et les arguments */ private Stack o1_emetteurs, o2_emetteurs; private Stack o1_arguments, o2_arguments; public class Observateur implements Observer{ private Stack emetteurs = new Stack(); private Stack arguments = new Stack(); public Stack emetteurs() { return emetteurs; } public Stack arguments() { return arguments; } public void update( Observable o, Object arg ) { emetteurs.push( o.toString() ); arguments.push( arg ); } } public class SujetObservable extends Observable { private String nom; public SujetObservable( String nom ) { this.nom = nom; } public String toString() { return nom; } protected void setChanged() { super.setChanged(); } } protected void setUp() // throws java.lang.Exception { s1 = new SujetObservable( "s1" ); s2 = new SujetObservable( "s2" ); o1 = new Observateur(); o1_emetteurs = o1.emetteurs(); o1_arguments = o1.arguments(); o2 = new Observateur(); o2_emetteurs = o2.emetteurs(); o2_arguments = o2.arguments(); } protected void tearDown() // throws java.lang.Exception { // Libérez ici les ressources engagées par setUp() }
import java.applet.Applet; import java.awt.Button; import java.awt.event.*; public class Observateur2Boutons extends Applet { private Button boutonA, boutonB; // déclaration des 3 ObservateurDeBouton <--- à compléter public void init() { boutonA = new Button( "A" ); // enregistrement des 3 observateurs du bouton A <--- à compléter boutonB = new Button("B"); // enregistrement de l'observateur du bouton B <--- à compléter add( boutonA ); add( boutonB ); // sur l'IHM } // init() } // Observateur2Boutons public class ObservateurDeBouton implements ActionListener { public void actionPerformed( ActionEvent e ) { System.out.println( "evenement bouton " + e.getActionCommand() + " : " + this ); } // actionPerformed() } // ObservateurDeBouton
public class Compteur { private int max, val = 0; private ObservateurDeCompteur observateur; public Compteur( int max ) { this.max = max; } public void reset() { /* à compléter */ } public int getVal() { /* à compléter */ } public void setVal( int val ) { /* à compléter */ } public void inc() { /* à compléter */ } public void dec() { /* à compléter */ } public void enregistrerCommeObservateur( ObservateurDeCompteur observateur ) { this.observateur = observateur; notifierALObservateur(); } // enregistrerCommeObservateur() private void notifierALObservateur() { observateur.compteurAChange( this ); } } // Compteur
// ObservateurDeCompteur.java public interface ObservateurDeCompteur { public void compteurAChange( Compteur ctr ); }
import java.applet.*; import java.awt.*; import java.awt.event.*; public class IHMCompteur extends Applet implements ActionListener, ObservateurDeCompteur { private Button incButton, decButton; private TextField valField; private Compteur ctr; public void init() { setBackground( Color.white ); incButton = new Button( "+" ); decButton = new Button( "-" ); valField = new TextField(); Panel p = new Panel(); p.setLayout( new BorderLayout() ); p.add( incButton, "North" ); p.add( decButton, "South" ); /* à compléter */ // Enregistrer le Contrôleur comme "écouteur" pour chacun des boutons add( p ); add( valField ); ctr = new Compteur( 10 ); /* à compléter */ // Enregistrer la Vue comme observateur } // init() public void actionPerformed( ActionEvent e ) // le Controle : appels de ctr.inc() et ctr.dec() { if ( e.getSource() == incButton ) /* à compléter */ } // actionPerformed() // la vue (ObservateurDeCompteur) ==> gestion de valField : /* à compléter */ } // IHMCompteur