1 1 Université Evry Val d'Essonne Y. Laborde Une calculatrice (sans MVC) fonctionnant sur la base d'un modèle en Java
2 2 Université Evry Val d'Essonne Y. Laborde Principe d'un modèle class ModelCalc1 Variables d'état privées : - hasMemoryStored : boolean - memoryValue : double etc. Méthodes publics : + isMemoryOperation(String opName) : boolean + doMemoryOperation(String opName) : boolean + hasMemoryStored() : boolean etc. Méthodes privées : - doMemoryStore() : boolean - doMemoryAdd() : boolean - doMemoryClear() : boolean - doMemoryRecall() : boolean etc. class ModelCalc1 Variables d'état privées : - hasMemoryStored : boolean - memoryValue : double etc. Méthodes publics : + isMemoryOperation(String opName) : boolean + doMemoryOperation(String opName) : boolean + hasMemoryStored() : boolean etc. Méthodes privées : - doMemoryStore() : boolean - doMemoryAdd() : boolean - doMemoryClear() : boolean - doMemoryRecall() : boolean etc. Notations UML utilisées $ = static+= public ƒ= final#= protected -= private = package Notations UML utilisées $ = static+= public ƒ= final#= protected -= private = package un modèle de calculatrice doit : Gérer l'état courant de la calculatrice en interne Fournir des méthodes publics qui permettent de piloter la calculatrice depuis une interface graphique Eventuellement posséder des méthodes privées pour réaliser l'algorithmique consécutive aux demandes d'action via l'interface graphique un modèle de calculatrice doit : Gérer l'état courant de la calculatrice en interne Fournir des méthodes publics qui permettent de piloter la calculatrice depuis une interface graphique Eventuellement posséder des méthodes privées pour réaliser l'algorithmique consécutive aux demandes d'action via l'interface graphique Ici, pour ce qui concerne la partie mémoire de la calculatrice
3 3 Université Evry Val d'Essonne Y. Laborde Exposition du modèle class ModelCalc1 Variables d'état privées : - hasMemoryStored : boolean - memoryValue : double etc. Méthodes publics : + isMemoryOperation(String opName) : boolean + doMemoryOperation(String opName) : boolean + hasMemoryStored() : boolean etc. Méthodes privées : - doMemoryStore() : boolean - doMemoryAdd() : boolean - doMemoryClear() : boolean - doMemoryRecall() : boolean etc. class ModelCalc1 Variables d'état privées : - hasMemoryStored : boolean - memoryValue : double etc. Méthodes publics : + isMemoryOperation(String opName) : boolean + doMemoryOperation(String opName) : boolean + hasMemoryStored() : boolean etc. Méthodes privées : - doMemoryStore() : boolean - doMemoryAdd() : boolean - doMemoryClear() : boolean - doMemoryRecall() : boolean etc. pour exposer le modèle, il est bon de définir une interface Java ne montrant que les méthodes publics utiles à son pilotage interface IModelCalc Méthodes publics : isMemoryOperation(String opName) : boolean doMemoryOperation(String opName) : boolean hasMemoryStored() : boolean etc. interface IModelCalc Méthodes publics : isMemoryOperation(String opName) : boolean doMemoryOperation(String opName) : boolean hasMemoryStored() : boolean etc. Ici, pour ce qui concerne la partie mémoire de la calculatrice En Java, il est fortement conseillé de ne pas utiliser le modificateur public dans les interfaces (il s'entend par défaut)
4 4 Université Evry Val d'Essonne Y. Laborde le pilotage du modèle est réalisé à travers son interface Java C'est la seule partie visible depuis les interfaces graphiques le pilotage du modèle est réalisé à travers son interface Java C'est la seule partie visible depuis les interfaces graphiques Pilotage du modèle ModelCalc1 PanelAfficheur PanelOperations PanelMemoire IModelCalc isMemoryOperation(String opName) : boolean doMemoryOperation(String opName) : boolean hasMemoryStored() : boolean etc. IModelCalc isMemoryOperation(String opName) : boolean doMemoryOperation(String opName) : boolean hasMemoryStored() : boolean etc. On entend par "pilotage" toutes les opérations qui permettent de : - Tester si une opération est permise (tests) - Faire évoluer l'état interne du modèle (actions) - Récupérer partiellement l'état interne courant (informations) ex :imodel.isMemoryOperation("M+") ex :imodel.doMemoryOperation("MC") ex :imodel.hasMemoryStored()
5 5 Université Evry Val d'Essonne Y. Laborde Plusieurs actions sur la "mémoire" sont possibles. Le modèle les propose au travers d'une unique méthode : doMemoryOperation(String opName) : boolean Plusieurs actions sur la "mémoire" sont possibles. Le modèle les propose au travers d'une unique méthode : doMemoryOperation(String opName) : boolean Conventions entre modèle et pilote PanelMemoire IModelCalc L'action désirée doit être donnée sous la forme d'un String. Il faut donc fixer des conventions sur les String à fournir à la méthode. Ces conventions auront avantage à être présentées à travers des constantes de classe du modèle et, mieux encore, à être intégrées à l'interface du modèle. Ces conventions auront avantage à être présentées à travers des constantes de classe du modèle et, mieux encore, à être intégrées à l'interface du modèle.
6 6 Université Evry Val d'Essonne Y. Laborde Conventions entre modèle et pilote interface IModelCalcConstants Constantes publics : /** Intitulés des opérations mémoire. * - Additionner la valeur de l'afficheur à la mémoire (M+) * - Placer la valeur de l'afficheur en mémoire (MS) * - Rappeler la mémoire vers l'afficheur (MR) * - Effacer la mémoire (MC) */ $ƒ nameOpsMemory : String[] = { "M+", "MS", "MR", "MC" } etc. interface IModelCalcConstants Constantes publics : /** Intitulés des opérations mémoire. * - Additionner la valeur de l'afficheur à la mémoire (M+) * - Placer la valeur de l'afficheur en mémoire (MS) * - Rappeler la mémoire vers l'afficheur (MR) * - Effacer la mémoire (MC) */ $ƒ nameOpsMemory : String[] = { "M+", "MS", "MR", "MC" } etc. les conventions sont intégrées dans une interface Java spécifique interface IModelCalc extends IModelCalcConstants Méthodes publics : isMemoryOperation(String opName) : boolean doMemoryOperation(String opName) : boolean hasMemoryStored() : boolean etc. interface IModelCalc extends IModelCalcConstants Méthodes publics : isMemoryOperation(String opName) : boolean doMemoryOperation(String opName) : boolean hasMemoryStored() : boolean etc. En Java, il est fortement conseillé de ne pas utiliser le modificateur public dans les interfaces (il s'entend par défaut) l'interface des méthodes publics du modèle étend celle des conventions ex: IModelCalcConstants.nameOpsMemory[2] vaut "MR"
7 7 Université Evry Val d'Essonne Y. Laborde Passons à la vue : le panel "mémoire" class PanelMemoire extends java.awt.Panel implements java.awt.event.ActionListener /** Cette classe représente un panel contenant : * - un label indiquant la présence d'une valeur en mémoire, * - un bouton M+ (memory add), * - un bouton MS (memory store), * - un bouton MR (memory read or recall), * - un bouton MC (memory clear). */ class PanelMemoire extends java.awt.Panel implements java.awt.event.ActionListener /** Cette classe représente un panel contenant : * - un label indiquant la présence d'une valeur en mémoire, * - un bouton M+ (memory add), * - un bouton MS (memory store), * - un bouton MR (memory read or recall), * - un bouton MC (memory clear). */ Il sera implémenté à l'aide de la classe PanelMemoire qui sera également l' ActionListener des boutons mémoire. Ce panel a pour responsabilité : - de déclencher les actions mémoire demandées par l'utilisateur vers le modèle, - d'enclencher la mise à jour de l'afficheur en conséquence. Ce panel a pour responsabilité : - de déclencher les actions mémoire demandées par l'utilisateur vers le modèle, - d'enclencher la mise à jour de l'afficheur en conséquence.
8 8 Université Evry Val d'Essonne Y. Laborde class PanelMemoire extends Panel implements ActionListener { /** Version de la classe. */ private static final long serialVersionUID = 1L; Les ATTRIBUTS statiques de la classe Les ATTRIBUTS objets de la classe /** Attribut pour référer au modèle. */ private IModelCalc model; /** Attribut pour référer à l'afficheur. */ private IPanelAfficheur afficheur; /** Attribut pour référer au label indiquant la présence d'une valeur mémorisée. */ private Label displayMemState;
9 9 Université Evry Val d'Essonne Y. Laborde class PanelMemoire extends Panel implements ActionListener { /** Constructeur du panel. model */ public PanelMemoire ( IModelCalc model, IPanelAfficheur afficheur ) { // Conserver le model et l'afficheur this.model = model; this.afficheur = afficheur; // Créer et connecter tous les composants addAllComponents (); } Le CONSTRUCTEUR du panel mémoire
10 Université Evry Val d'Essonne Y. Laborde class PanelMemoire extends Panel implements ActionListener { /** Méthode interne ajoutant et organisant les composants dans le panel. */ private void addAllComponents () { // Définir un grid layout de 5 lignes et 2 colonnes setLayout (new GridLayout (5, 1, 2, 2)); // Donner une couleur au panel setBackground (Color.YELLOW); // Créer l'indicateur de présence de mémoire this.displayMemState = new Label ("", Label.CENTER); this.displayMemState.setBackground (Color.BLACK); // L'ajouter au panel add (this.displayMemState); // Créer et connecter tous les boutons for( String nameBut : IModelCalcConstants.nameOpsMemory ) { // Créer le bouton (nameBut sera aussi l'action command du bouton) Button b = new Button (nameBut); // L'ajouter au panel add (b); // Définir le listener du bouton b.addActionListener (this); } La CONSTRUCTION et l'AJOUT des composants du panel mémoire
11 Université Evry Val d'Essonne Y. Laborde class PanelMemoire extends Panel implements ActionListener { /* (non-Javadoc) java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */ public void actionPerformed (ActionEvent e ) { // Retrouver l'opération à réaliser (actionCommand = texte du bouton) String opName = e.getActionCommand (); // Vérifier que l'opération soit bien comprise par le modèle (optionnel) if( this.model.isMemoryOperation (opName) ) { // Lancer l'opération mémoire if( this.model.doMemoryOperation (opName) ) { // L'opération a maintenant été réalisée // => l'afficheur peut avoir à changer // (cas de MR) this.afficheur.resetFromModel (); // => l'indicateur mémoire aussi (cas de MS, M+, MC) if( this.model.hasMemoryStored () ) { this.displayMemState.setText ("M"); } else { this.displayMemState.setText (""); } La méthode d'action sur les boutons du panel mémoire Ici, il faut maintenant mettre à jour tous les affichages qui auront changés du fait de l'opération mémoire réalisée Ici, ne sachant s'il y a une valeur en mémoire, il faut le demander au modèle.....et mettre l'indicateur à jour en fonction de cela Ici, ne sachant s'il y a une valeur en mémoire, il faut le demander au modèle.....et mettre l'indicateur à jour en fonction de cela
12 Université Evry Val d'Essonne Y. Laborde Une calculatrice (avec MVC) fonctionnant sur la base d'un modèle en Java
13 Université Evry Val d'Essonne Y. Laborde Pourquoi passer au MVC ? Les composants déclencheurs d'action n'auront plus à se préoccuper de mettre à jour les autres composants. Les vues devront interpréter des demandes de mise à jour définies conventionnellement par le modèle. en nous affranchissant de déterminer quels autres composants doivent être mis à jour après une modification du modèle en généralisant la notion de vues conformes à un modèle donné Le paradigme MVC permet d'aller encore plus loin : Le MVC apporte un gain en modularité et donc en sûreté, en maintenance et en longévité des applicatifs. Le MVC apporte un gain en modularité et donc en sûreté, en maintenance et en longévité des applicatifs.
14 Université Evry Val d'Essonne Y. Laborde Comment ? Principe du MVC les vues envoient des demandes d'action au modèle les vues envoient des demandes d'action au modèle PanelAfficheur PanelOperations PanelMemoire Observable le modèle renvoie des demandes de mise à jour à toutes les vues inscrites le modèle renvoie des demandes de mise à jour à toutes les vues inscrites SEULES les vues qui le désireront se mettrons à jour......les autres laisseront passer le message... Observer ModelCalc1 IModelCalc...ainsi, tous les composants auront l'occasion de se mettre à jour. Par la méthode standardisée dans l'interface Observer : public void update(Observable o, Object arg) Par des méthodes définies dans l'interface du modèle. Voir cours MVC
15 Université Evry Val d'Essonne Y. Laborde La mise à jour avec paramètre Le modèle envoie une demande de mise à jour à toutes les vues à l'aide de la méthode : public void update(Observable o, Object arg) Le modèle envoie une demande de mise à jour à toutes les vues à l'aide de la méthode : public void update(Observable o, Object arg) Voir cours MVC Pour que les vues comprennent l'argument Object transmis, il faut fixer des conventions sur la nature et le contenu de arg. Comme précédemment, ces conventions auront avantage à être présentées à travers des constantes de classe du modèle et, mieux encore, à être intégrées à l'interface du modèle. Comme précédemment, ces conventions auront avantage à être présentées à travers des constantes de classe du modèle et, mieux encore, à être intégrées à l'interface du modèle. interface IModelCalcConstants Constantes publics : /** Conventions de mise à jour des vues par la méthode update * => paramètre Object arg */ $ƒ WC_RESULT_HAS_CHANGED : String = "WC_RESULT_HAS_CHANGED" $ƒ WC_MEMORY_HAS_CHANGED : String = "WC_MEMORY_HAS_CHANGED" etc. interface IModelCalcConstants Constantes publics : /** Conventions de mise à jour des vues par la méthode update * => paramètre Object arg */ $ƒ WC_RESULT_HAS_CHANGED : String = "WC_RESULT_HAS_CHANGED" $ƒ WC_MEMORY_HAS_CHANGED : String = "WC_MEMORY_HAS_CHANGED" etc. les conventions sont intégrées dans l'interface des constantes du modèle
16 Université Evry Val d'Essonne Y. Laborde La mise à jour avec paramètre Voir cours MVC interface IModelCalcConstants Constantes publics : // Conventions de mise à jour des vues par la méthode update // => le paramètre Object arg est de classe WhatChanged /** Changement du résultat * => l'attribut mess est WC_RESULT_HAS_CHANGED * => l'attribut o1 est un Double contenant le nouveau résultat */ static final String WC_RESULT_HAS_CHANGED = "WC_RESULT"; /** Changement dans la valeur mémorisée * => l'attribut mess est WC_MEMORY_HAS_CHANGED * => l'attribut o1 est un Boolean contenant la présence d'une valeur en mémoire */ static final String WC_MEMORY_HAS_CHANGED = "WC_MEMORY"; etc. interface IModelCalcConstants Constantes publics : // Conventions de mise à jour des vues par la méthode update // => le paramètre Object arg est de classe WhatChanged /** Changement du résultat * => l'attribut mess est WC_RESULT_HAS_CHANGED * => l'attribut o1 est un Double contenant le nouveau résultat */ static final String WC_RESULT_HAS_CHANGED = "WC_RESULT"; /** Changement dans la valeur mémorisée * => l'attribut mess est WC_MEMORY_HAS_CHANGED * => l'attribut o1 est un Boolean contenant la présence d'une valeur en mémoire */ static final String WC_MEMORY_HAS_CHANGED = "WC_MEMORY"; etc. les conventions sont intégrées dans l'interface des constantes du modèle De plus, l'argument Object arg sera l'occasion de transmettre : - la nature du changement survenu dans le modèle - les données utiles pour effectuer la mise à jour côté vue Et cela, grâce à la classe : WhatChanged (vue en cours) De plus, l'argument Object arg sera l'occasion de transmettre : - la nature du changement survenu dans le modèle - les données utiles pour effectuer la mise à jour côté vue Et cela, grâce à la classe : WhatChanged (vue en cours)
17 Université Evry Val d'Essonne Y. Laborde java.util.Observer#update(Observable o, Object arg) */ public void update (Observable o, Object arg){ // Retrouver l'argument WhatChanged if ( arg instanceof WhatChanged ) { // Secure cast ! WhatChange wc = (WhatChanged)arg; // Tester si le message est à prendre en compte if ( wc.mess.equals( IModelCalcWC.WC_MEMORY_HAS_CHANGED ) ) { // L'indicateur mémoire a changé ! if( wc.o1 ) { this.displayMemState.setText ("M"); } else { this.displayMemState.setText (""); } java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */ public void actionPerformed (ActionEvent e) { // Retrouver l'opération à réaliser (actionCommand = texte du bouton) String opName = e.getActionCommand (); // Lancer l'opération mémoire this.model.doMemoryOperation (opName); } RETOUR sur la méthode d'action sur les boutons du panel mémoire Maintenant, on se contente de réaliser l'opération mémoire demandée (opName vaut "M+", "MS", "MR" ou " MC") Maintenant, on se contente de réaliser l'opération mémoire demandée (opName vaut "M+", "MS", "MR" ou " MC") Ici, l'objet o1 de WhatChanged indique s'il y a une valeur en mémoire on met l'indicateur à jour en fonction de cela Ici, l'objet o1 de WhatChanged indique s'il y a une valeur en mémoire on met l'indicateur à jour en fonction de cela Ici, on doit retrouver l'objet WhatChanged......et examiner son message Ici, on doit retrouver l'objet WhatChanged......et examiner son message AJOUT de la méthode de mise à jour du panel mémoire
18 Université Evry Val d'Essonne Y. Laborde Que reste-t-il à faire ? Dériver le modèle de java.util.Observable class ModelCalc1 extends java.util.Observable class ModelCalc1 extends java.util.Observable Faire que tous les panels déclarent implémenter java.util.Observer class PanelMemoire extends Panel implements ActionListener, java.util.Observer class PanelMemoire extends Panel implements ActionListener, java.util.Observer Faire que les méthodes du modèle invoquent notifyObservers( Object arg ) avec un argument WhatChange... setChange(true);... notifyObservers ( new WhatChange ( IModelCalcConstants. WC_MEMORY_HAS_CHANGED, new Boolean (hasMemoryStored()) ) );... setChange(true);... notifyObservers ( new WhatChange ( IModelCalcConstants. WC_MEMORY_HAS_CHANGED, new Boolean (hasMemoryStored()) ) ); Faire que tous les panels implémentent la méthode public void update(...) Voir diapositive précédente Inscrire les panels auprès des modèles avec : add(Observer o) (dans les panels) : this.model.add (this);