1Université Evry Val d'EssonneY
. Lab
ord
e
Une calculatrice (sans MVC)
fonctionnant sur la base d'un modèle
en Java
2Université Evry Val d'EssonneY
. Lab
ord
e
Principe d'un modèle
class ModelCalc1Variables 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() : booleanetc.
class ModelCalc1Variables 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() : booleanetc.
Notations UML utilisées$ = static + = publicƒ = final # = protected
- = private<rien> = package
Notations UML utilisées$ = static + = publicƒ = final # = protected
- = private<rien> = 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
3Université Evry Val d'EssonneY
. Lab
ord
e
Exposition du modèle
class ModelCalc1Variables 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() : booleanetc.
class ModelCalc1Variables 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() : booleanetc.
➽ pour exposer le modèle, il est bon de définir une interface Java ne montrant que les méthodes publics utiles à son pilotage
➽ 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 IModelCalcMéthodes publics :isMemoryOperation(String opName) : boolean doMemoryOperation(String opName) : booleanhasMemoryStored() : boolean etc.
interface IModelCalcMéthodes publics :isMemoryOperation(String opName) : boolean doMemoryOperation(String opName) : booleanhasMemoryStored() : 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)
4Université Evry Val d'EssonneY
. Lab
ord
e
➽ 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
ModelCalc1ModelCalc1
PanelAfficheurPanelAfficheur
PanelOperationsPanelOperations
PanelMemoirePanelMemoire
IModelCalcisMemoryOperation(String opName) : boolean doMemoryOperation(String opName) : booleanhasMemoryStored() : boolean etc.
IModelCalcisMemoryOperation(String opName) : boolean doMemoryOperation(String opName) : booleanhasMemoryStored() : boolean etc.
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()
5Université Evry Val d'EssonneY
. Lab
ord
e
➽ 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
PanelMemoirePanelMemoireIModelCalcIModelCalc
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.➽ 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èleet, mieux encore, à être intégrées à l'interface du modèle.
6Université Evry Val d'EssonneY
. Lab
ord
e
Conventions entre modèle et piloteinterface 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 IModelCalcConstantsConstantes 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➽ les conventions sont intégrées dans une interface Java spécifique
interface IModelCalcextends IModelCalcConstants
Méthodes publics :isMemoryOperation(String opName) : boolean doMemoryOperation(String opName) : booleanhasMemoryStored() : boolean etc.
interface IModelCalcextends IModelCalcConstants
Méthodes publics :isMemoryOperation(String opName) : boolean doMemoryOperation(String opName) : booleanhasMemoryStored() : 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
➽ l'interface des méthodes publics du modèle étend celle des conventions
ex: IModelCalcConstants.nameOpsMemory[2] vaut "MR"
7Université Evry Val d'EssonneY
. Lab
ord
e
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 PanelMemoirequi sera également l'ActionListener des boutons mémoire.
➽ Il sera implémenté à l'aide de la classe PanelMemoirequi 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.
8Université Evry Val d'EssonneY
. Lab
ord
e
class PanelMemoireextends Panelimplements ActionListener {
/** Version de la classe. */private static final long serialVersionUID = 1L;
➽ Les ATTRIBUTS statiques de la classe ➽ Les ATTRIBUTS statiques de la classe
➽ Les ATTRIBUTS objets 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;
9Université Evry Val d'EssonneY
. Lab
ord
e
class PanelMemoireextends Panelimplements ActionListener {
/** Constructeur du panel. * @param model */public PanelMemoire (
IModelCalc model, IPanelAfficheur afficheur ) {
// Conserver le model et l'afficheurthis.model = model;this.afficheur = afficheur;
// Créer et connecter tous les composantsaddAllComponents ();
}
➽ Le CONSTRUCTEUR du panel mémoire ➽ Le CONSTRUCTEUR du panel mémoire
10Université Evry Val d'EssonneY
. Lab
ord
e
class PanelMemoireextends Panelimplements 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 colonnessetLayout (new GridLayout (5, 1, 2, 2));
// Donner une couleur au panelsetBackground (Color.YELLOW);
// Créer l'indicateur de présence de mémoirethis.displayMemState = new Label ("", Label.CENTER);this.displayMemState.setBackground (Color.BLACK);// L'ajouter au paneladd (this.displayMemState);
// Créer et connecter tous les boutonsfor( String nameBut : IModelCalcConstants.nameOpsMemory ) {
// Créer le bouton (nameBut sera aussi l'action command du bouton)Button b = new Button (nameBut);// L'ajouter au paneladd (b);// Définir le listener du boutonb.addActionListener (this);
}}
➽ La CONSTRUCTION et l'AJOUT des composants du panel mémoire ➽ La CONSTRUCTION et l'AJOUT des composants du panel mémoire
11Université Evry Val d'EssonneY
. Lab
ord
e
class PanelMemoireextends Panelimplements ActionListener {
/* (non-Javadoc) * @see 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émoireif( 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 ➽ 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
12Université Evry Val d'EssonneY
. Lab
ord
e
Une calculatrice (avec MVC)
fonctionnant sur la base d'un modèle
en Java
13Université Evry Val d'EssonneY
. Lab
ord
e
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 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é
en généralisant la notion de vues conformes à un modèle donné
Le paradigme MVC permet d'aller encore plus loin :Le paradigme MVC permet d'aller encore plus loin :
Le MVC apporte un gain en modularité et doncen sûreté, en maintenance et en longévité des applicatifs.
14Université Evry Val d'EssonneY
. Lab
ord
e
Comment ? Principe du MVC
PanelAfficheurPanelAfficheur
PanelOperationsPanelOperations
PanelMemoirePanelMemoireObservable
➽ 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
Observer
Observer
ModelCalc1ModelCalc1
IModelCalcIModelCalc
...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
❶
❷
15Université Evry Val d'EssonneY
. Lab
ord
e
La mise à jour avec paramètreLe 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.➽ 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èleet, mieux encore, à être intégrées à l'interface du modèle.
interface IModelCalcConstantsConstantes 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 IModelCalcConstantsConstantes 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
➽ les conventions sont intégrées dans l'interface des constantes du modèle
16Université Evry Val d'EssonneY
. Lab
ord
e
La mise à jour avec paramètre Voir cours MVC
interface IModelCalcConstantsConstantes 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 IModelCalcConstantsConstantes 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
➽ 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)
17Université Evry Val d'EssonneY
. Lab
ord
e
/* @see java.util.Observer#update(Observable o, Object arg) */public void update (Observable o, Object arg){
// Retrouver l'argument WhatChangedif ( arg instanceof WhatChanged ) {
// Secure cast !WhatChange wc = (WhatChanged)arg;// Tester si le message est à prendre en compteif ( 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 ("");}
}
}
/* @see 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émoirethis.model.doMemoryOperation (opName);
}
➽ RETOUR sur la méthode d'action sur les boutons du panel mémoire ➽ 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")
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
➽ AJOUT de la méthode de mise à jour du panel mémoire ➽ AJOUT de la méthode de mise à jour du panel mémoire
18Université Evry Val d'EssonneY
. Lab
ord
e
Que reste-t-il à faire ? Dériver le modèle de
java.util.Observable Dériver le modèle de
java.util.Observableclass ModelCalc1
extends java.util.Observable
Faire que tous les panels déclarent implémenterjava.util.Observer
Faire que tous les panels déclarent implémenterjava.util.Observer
class PanelMemoireextends Panelimplements ActionListener
, java.util.Observer
Faire que les méthodes du modèle invoquent notifyObservers(Object arg) avec un argument WhatChange
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()) ) );
Faire que tous les panels implémentent la méthodepublic void update(...)
Faire que tous les panels implémentent la méthodepublic void update(...)
Voir diapositive précédente Inscrire les panels auprès des
modèles avec :add(Observer o)
Inscrire les panels auprès des modèles avec :add(Observer o) (dans les panels) : this.model.add (this);