NSY 102




TP n° 1

Introduction, mise en oeuvre, patron Observateur (Voir sujet ici)


>  Profil


Prénom :  Rivo.....ka
Nom :  RA......LY
Matricule :  03-...86
Mél. :  ....@.....


>  Réponses aux questions


QUESTION 1 : Pattern Observateur/Observé

Dans cette question, on dispose des participants suivant :
  • L'observé : la classe Subject qui est observable.
  • L'observateur : ici implémenté par l'interface Observer.
  • L'observé concret : la classe ConcreteSubject.
  • L'observateur concret : la classe ConcreteObserver qui utilise une référence du sujet concret qu'il observe, et réagi à chaque mise à jour.
    On nous demande de compléter les trois (3) méthodes de test de la classe de test "PatternObservateur"
    qui sont : test1(), test2() et test3().

  • Voici les extraits de codes-sources en guise d'éléments de réponses :

    - Méthode test1() :

    01             /**
    02              * ==> 1 liste et 2 observateurs.
    03              * Vérifier ci-dessous, au moins que les deux observateurs ont été bien notifiés.
    04              
    05              * Rappel : Le dernier élément entré sera le premier à sortir de la pile !
    06              */
    07 
    08             assertFalse(o1.senders().empty() && o1.arguments().empty());
    09             assertFalse(o2.senders().empty() && o2.arguments().empty());  
    10             
    11             // observateur #1 -------------------------------
    12             assertEquals(l1,     o1.senders().pop());
    13             assertEquals(" 1 ",  o1.arguments().pop());
    14             
    15             assertEquals(l1,     o1.senders().pop());
    16             assertEquals("test", o1.arguments().pop());
    17              
    18             // observateur #2 -------------------------------
    19             assertEquals(l1,     o2.senders().pop());        
    20             assertEquals(" 1 ",  o2.arguments().pop());
    21             
    22             assertEquals(l1,     o2.senders().pop());
    23             assertEquals("test", o2.arguments().pop());

    - Méthode test2():

    01             /**
    02              * ==> 2 listes et 1 observateur.
    03              * Vérifier ci-dessous, (au moins) que l'observateur a été 
    04              * bien notifié par les deux listes (l1 et l2 :> ConcreteSubject).
    05              
    06              * Rappel : Le dernier élément entré sera le premier à sortir de la pile !           
    07              */
    08             //------------------------------------------------------   
    09             assertFalse(o.senders().empty() && o.arguments().empty());
    10             
    11             assertEquals(l2,      o.senders().pop());
    12             assertEquals(" B ",   o.arguments().pop());
    13             assertEquals(l2,      o.senders().pop());
    14             assertEquals("testB", o.arguments().pop());
    15 
    16             assertEquals(l1,      o.senders().pop());
    17             assertEquals(" A ",   o.arguments().pop());
    18             assertEquals(l1,      o.senders().pop());
    19             assertEquals("testA", o.arguments().pop());

    - Méthode test3():

    01         /**
    02          * ==> 2 listes et 2 observateurs.
    03          * Vérifier le bon fonctionnement de :
    04          *          - countObservers()
    05          *          - deleteObserver(Observer o)
    06          *          - deleteObservers()
    07          */
    08         // -----------------------------------------------------------
    09         // Nombre d'observateurs dans les listes l1 et l2.
    10         l1.countObservers();
    11         l2.countObservers();
    12 
    13         // Supprime l'observateur "o1" des listes l1 et l2.   
    14         l1.deleteObserver(o1);
    15         l2.deleteObserver(o1);
    16         
    17         // Suppression de tous les Observateurs dans les listes l1 et l2.
    18         l1.deleteObservers();
    19         l2.deleteObservers();

    Ci-dessous se trouvent les fichiers sources (*.java) et la documentation (*.html) correspondante :

    Code-source complet :
    Documentation :
      question1/ConcreteSubject.java
    Voir
      question1/ConcreteObserver.java Voir
     question1/PatternObservateur.java
    Voir



    QUESTION 2 : Une mise en oeuvre

    On nous demande de compléter l'architecture proposée dans le sujet.
    Nous allons d'abord, rajouter les liens nécessaires entre les différents objets qu'on a.
    Voici le résultat obtenu :


    NB : La présence de certains liens ici ont une explication, que je donnerai plus bas.

    La classe Sensor

    Elle représente un capteur, dont le changement de valeur s'effectue par la méthode setValue().
    Pour cette classe, on nous demande de compléter le code manquant.

    1. On s'assure déjà d'importer les bonnes classes et les bons paquetages.
    1 package question2;
    2 
    3 import java.util.Observable;
    4 import question2.MessageEvent; // facultatif

    2. Maintenant, nous étendons Observable. On disposera alors des quelques méthodes implémentées dans la classe Observable, à savoir : setChanged(), notifyObservers().
    1 public class Sensor extends Observable

    3. Outre les méthodes déjà implémentées dans la classe Sensor, on nous demande de compléter la méthode setValue(int Value).
    On va la compléter comme suit :
    - on va stocker la valeur dans une variable.
    - puis, on va encapsuler (à l'aide de MessageEvent) la valeur du capteur dans un message, pour être envoyée aux observateurs. On saura ainsi de quel capteur et de quelle valeur il s'agit.
    - on va notifier les observateurs de ce changement de valeur en lui envoyant cet objet-message.
    01     public void setValue(int value){
    02         // Valeur actuelle du capteur.
    03         this.value = value;
    04         
    05         // L'état du capteur a changé.
    06         setChanged();
    07         
    08         // Encapsulation de la valeur émise par le capteur, et ...
    09        
    // Notification des observateurs
    10        
    notifyObservers(new MessageEvent(this, Integer.toString(value) ));
    11    
    }      

    Remarque : L'utilisation de la classe MessageEvent dans la classe Sensor justifie le lien d'utilisation entre ces deux classes, dans l'architecture proposée plus haut.

    La classe SensorObserver

    La classe SensorObserver représente les observateurs possibles d'un capteur, notifiés à chaque changement de valeur.
    Complétons le code manquant de cette classe.
    1. Importons les bonnes classes et les bons paquetages qu'il nous faut :
    1 package question2;
    2 
    3 import java.util.Observable;
    4 import java.util.Observer;
    5 import question2.Sensor; // facultatif
    6 import question2.MessageEvent; // facultatif
    2. Maintenant on implémente l'interface Observer. L'objet instancié à partir de la classe SensorObserver peut alors "se comporter comme un observateur".
    1 public class SensorObserver implements Observer

    3. Cette classe n'a guère de méthodes, on va donc réécrire la méthode existant dans l'interface de façon plus personnalisée. Dans cette méthode personnalisée, on doit tester si l'observateur passé en paramètre est bien un objet instancié à partir de la classe Sensor. Même chose pour l'argument, il faut qu'il soit une instance de la classe MessageEvent (d'où les liens d'utilisation dans le schéma d'architecture entre la classe Sensor et SensorObserver, et entre la classe MessageEvent et SensorObserver). Si le test réussi, alors on récupère le message. Voici, sans plus attendre, le code de la méthode update() :
    01     public void update(Observable o_obs, Object o_arg
    02     {
    03       // Est-ce une instance de la classe Sensor ?
    04       if(o_obs != null && o_obs instanceof Sensor){            
    05         // Est-ce une instance de la classe MessageEvent ?
    06         if(o_arg != null && o_arg instanceof MessageEvent){ 
    07           // Le "message" passé en argument.
    08           this.arg = o_arg; 
    09         }
    10       }
    11     }

    La classe MessageEvent

    Elle représente le message encapsulé par l'évènement levé lors de la notification.
    Elle peut retourner ce message sous forme de chaine de caractères lisibles par l'utilisateur.
    Le code-source est disponible ici.

    La classe UnTest

    Elle représente une classe de test. Elle dérive de sa classe mère TestCase (voir documentation cette dernière en cliquant ici).
    Les classes à tester se trouvent dans le même paquetage. Il faut, pour effectuer un test, créer une méthode de test. Dans ces méthodes de test, on met un ensemble d'assertions qui doivent retourner VRAI pour qu'un test réussisse à l'aide de assertTrue(<Boolean boolExpression>).
    On a aussi la possibilité de définir des engagements dans les méthodes setUp() et tearDown(). Ces méthodes sont respectivement appelées avant (pour initialiser les engagements) et après la méthode de test (pour détruire ces engagements).
    Le code-source de la classe UnTest est disponible ici.

    Ci-dessous se trouvent les fichiers sources (*.java) et la documentation (*.html) correspondante :

    Code-source complet :
    Documentation :
      question2/Sensor.java
    Voir
      question2/SensorObserver.java Voir
     question2/MessageEvent.java
    Voir
     question2/UnTest.java
    Voir



    >  Commentaires



    Aucun commentaire particulier, sinon un article qu'il m'a semblé intéressant à lire sur le site du MSDN de Microsoft.
    Voici un extrait :

    Pourquoi les modèles de conception ?

    Bien qu’il n’y ait rien de magique dans les modèles de conception, ce sont des outils extrêmement puissants pour développeurs ou architectes qui s’embarquent dans le développement de projet. Les modèles de conception garantissent que les problèmes soient adressés par des solutions reconnues et acceptées de tous.

    La force fondamentale des modèles reste dans le fait, que la plupart des problèmes ont vraisemblablement été rencontré et résolu par un ensemble d’individus et par la communauté des développeurs. En d’autres termes, les modèles fournissent un mécanisme de partage de solutions qui fonctionnent entre les développeurs et les organisations. Peu importe l’origine des modèles, c’est un formidable levier de l’expérience collective. Ils garantissent, l’implémentation rapide d’un code robuste, et la réduction des erreurs lors de la conception ou de l’implémentation. De plus, les modèles de conceptions offrent une sémantique commune entre chaque membre d’une équipe. Ceux qui ont été impliqués dans un projet un temps soit peu important, savent, qu’avoir un jeu commun de modèles, de syntaxes et de principes de développement, est critique pour l’achèvement du projet. Les modèles de conception, s’ils sont utilisés judicieusement, peuvent faire gagner un temps précieux.

    > Lire tout l'article ici.

    >  Difficultés rencontrées



    La première partie du TP a été résolue aisément dans l'ensemble.
    J'ai commis, néanmoins l'erreur de mettre un System.out.print() dans les blocs de tests !
    Cela provoque un avertissement et on nous recommande d'enlever ce genre de ligne de code.
    Aussi, je me suis permis de mettre un void dans le constructeur des classes, et lors d'un test sous BlueJ et lors d'une
    soumission/exportation du projet, je me retrouve  avec une exception NullPointerException !
    Il faut donc éviter de mettre void en tant que valeur de retour des constructeurs sous BlueJ,
    comme l'illustre la figure suivante :



    Une fois les erreurs corrigées, les tests de la première question ont tous été exécutés avec succès !

    Dans la deuxième partie du TP, je me suis un peu compliqué les choses dans la classe Sensor dans la méthode upDate().
    Je voulais écrire un code du genre :
    01     public void update(Observable o_obs, Object o_arg
    02     {
    03       // Est-ce une instance de la classe Sensor ?
    04       if(o_obs != null && o_obs instanceof Sensor){            
    05         // Est-ce une instance de la classe MessageEvent ?
    06         if(o_arg != null && o_arg instanceof MessageEvent){ 
    07           // Le "message" passé en argument.
    08           this.arg = ((MessageEvent)o_arg).getMessage();
    09         }
    10       }
    11     

    Il s'avère que ce code lève une exception inattendue lors de la soumission de la question. Impossible de caster un String en MessageEvent !!!. Et j'obtiens ainsi 3 échecs aux tests référents !
    Et pourtant les tests sous BlueJ ont tous réussi !
    Il a fallu donc que je résolve le problème en ne s'intéressant qu'à l'objet o_arg. Il a juste fallu stocker cet objet dans this.arg au lieu d'en faire un objet MessageEvent par casting et de n'en retenir que le message transmis en appelant la méthode getMessage().

    >  Références bibliographiques et web



    Références :
      Guide d'apprentissage : Tête la première / Design Patterns - [Edition EYROLLES-O'REILLY ® / ISBN 2-84177-350-7]
    Introduction aux designs pattern en Java (www.developpez.com)