IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Java Discussion :

Cherche pattern pour listeners (éviter les cycles)


Sujet :

Java

  1. #1
    Membre expert
    Avatar de ®om
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    2 815
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 815
    Points : 3 080
    Points
    3 080
    Par défaut Cherche pattern pour listeners (éviter les cycles)
    Salut,

    Déjà confronté plusieurs fois à ce problème, je cherche un pattern qui puisse résoudre mon problème.

    J'ai un modèle et une vue, chacun possédant des composants écoutables (par des listeners).

    Le modèle peut être modifié par la vue (par exemple, lors de la modification d'un textfield, un champ String est affecté sur le modèle), mais la vue peut aussi être modifiée par le modèle (si le champ String est affecté par autre chose, une autre vue par exemple, la vue doit se mettre à jour).

    Le problème est que si on code bêtement les listeners, avec le modèle qui écoute la vue et la vue qui écoute le modèle, dès qu'il y a une modification, on tourne en boucle :
    - le textfield est modifié
    - donc la String dans le modèle est modifiée
    - donc le textfield est modifié
    - donc la String dans le modèle est modifiée
    - donc le textfield est modifié
    - ...
    - merde on tourne en rond merde on tourne en rond merde on tourne en rond...

    Pour éviter ce cycle, j'avais pensé à ce pattern :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    public class JFrame { //par exemple
     
        private final Controller CONTROLLER = new Controller();
     
        public JFrame() {
            //...  
            unComposantDeLaVue.addActionListener(CONTROLLER);
            monComposantDeModele.addModeleListener(CONTROLLER);
        }
     
        private class Controller implements ActionListener, ModeleListener {
     
            private boolean modelAction;
            private boolean viewAction;
     
            @Override
            public void actionPerformed(ActionEvent e) {
                if(!modelAction) {
                    viewAction = true;
                    //modification du modèle
                    viewAction = false;
                }
            }
     
            @Override
            public void modeleChanged() {
                if(!viewAction) {
                    modelAction = true;
                    //modification de la vue
                    modelAction = false;
                }
            }
        }
    }
    Le problème, c'est qu'une modification sur la vue peut modifier le modèle, qui lui même pourra modifier AUTRE CHOSE sur la vue.

    Par exemple, si j'ai 2 JSpinner, représentant les valeurs A et B.
    Si je modifie graphiquement A, ça appelle une fonction sur le modèle, qui change évidemment A mais qui fait un calcul sur B. La modification de A ne doit pas se répercuter sur la vue (car c'est la source de l'action), mais par contre la modification de B doit être signalée à la vue...

    Connaissez-vous un joli pattern qui règle ce problème?

    Car là à part faire un booléen par composant graphique, qui serait associé à l'évènement correspondant sur le modèle, je ne vois pas...

    Une idée?



    EDIT: je crois que j'ai trouvé une solution (je testerai demain).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    public class JFrame { //par exemple
     
        private final Controller CONTROLLER = new Controller();
     
        public JFrame() {
            //...  
            unComposantDeLaVue.addActionListener(CONTROLLER);
            monComposantDeModele.addModeleListener(CONTROLLER);
        }
     
        private class Controller implements ActionListener, ModeleListener {
     
            private boolean modelAction;
            private Object viewActionSource;
     
            @Override
            public void actionPerformed(ActionEvent e) {
                if(!modelAction) {
                    viewActionSource = e.getSource();
                    //modification du modèle
                    viewActionSource = null;
                }
            }
     
            @Override
            public void modeleChanged() {
                if(viewActionSource != composantAssocié) {
                    modelAction = true;
                    //modification de la vue
                    modelAction = false;
                }
            }
        }
    }
    En gros, lorsque le modèle est modifié, on regarde si ça n'est pas le composant graphique susceptible de l'avoir modifié qui est source de l'évènement.

  2. #2
    Membre expérimenté Avatar de herve91
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    1 282
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 1 282
    Points : 1 608
    Points
    1 608
    Par défaut
    Les données du modèle "écoutables" pourraient être modélisées par des propriétés.
    Exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    class Model {
       private SwingPropertyChangeSupport changeSupport = new SwingPropertyChangeSupport(this);
     
       private String nom;
     
       public String getNom() {
         return nom;
       }
     
       public void setNom(String nom) {
         String oldNom = getNom();
         this.nom = nom;
         firePropertyChange("nom", oldNom, nom);
      }
     
      protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
        changeSupport.firePropertyChange(propertyName, oldValue, newValue);
      }
     
      public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
        changeSupport.addPropertyChangeListener(propertyName, listener);
      }
     
      public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
        changeSupport.removePropertyChangeListener(propertyName, listener);
      }
    }
    Dans ce cas, les listeners sont exécutés ssi il y a modification de la propriété "nom". Cela devrait éviter les bouclages, sauf si évidemment un listener sur la propriété "nom" s'amuse à faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    public void propertyChange(PropertyChangeEvent evt) {
       String nom = (String) evt.getNewValue();
       model.setNom(nom + ".");
    }
    ce qui la modifierait sans arrêt.

  3. #3
    Membre expert
    Avatar de ®om
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    2 815
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 815
    Points : 3 080
    Points
    3 080
    Par défaut
    Ah en fait sur les composants Swing, un setText() qui affecte le texte déjà présent ne provoque pas d'évènement. Pareil pour tous les composants, c'est pratique.
    Y'a pas de cycles en fait

  4. #4
    Membre émérite
    Avatar de gifffftane
    Profil pro
    Inscrit en
    Février 2007
    Messages
    2 354
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire (Rhône Alpes)

    Informations forums :
    Inscription : Février 2007
    Messages : 2 354
    Points : 2 582
    Points
    2 582
    Par défaut
    Moi aussi j'utilise un test de modification de propriété, un peu comme le suggère herve91 à ce que je comprends. Dans le setter je fais (quelquefois) la chose suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    public void setTruc(Truc truc)
    {
     if (!this.truc.equals(truc))
     {
      this.truc = truc;
      fireTrucChanged();
     }
    }
    Le résultat est bien souvent maqique, même quand on s'y attend.
    Mieux que Google, utilisez Sur Java spécialisé sur la plate-forme java !
    Pour réaliser vos applications Java dans le cadre de prestations, forfait, conseil, contactez-moi en message privé.

  5. #5
    Membre expert
    Avatar de ®om
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    2 815
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 815
    Points : 3 080
    Points
    3 080
    Par défaut
    En parlant de listeners, je me suis programmé aujourd'hui un petit générateur de listeners (car ça prend énormément de temps à chaque fois d'écrire tout ça, c'est très verbeux).

    Là, je passe de:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    LISTENABLE Simulator
     
    LISTENER SimulatorListener
    nextStep(*)
    numberOfAgentsChanged(int numberOfAgents [agents.size()])
    numberOfResourcesChanged(int numberOfResources [resources.size()])
     
    //pour les Event
    somethingHappened(SomethingEvent{*, int size [getSize()], boolean canDo +})
    à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    import javax.swing.event.EventListenerList;
     
    public class Simulator {
     
        private final EventListenerList LISTENERS = new EventListenerList();
     
        // class body
     
        public void addSimulatorListener(SimulatorListener listener) {
            LISTENERS.add(SimulatorListener.class, listener);
        }
     
        public void removeSimulatorListener(SimulatorListener listener) {
            LISTENERS.remove(SimulatorListener.class, listener);
        }
     
        protected SimulatorListener[] getSimulatorListeners() {
            return LISTENERS.getListeners(SimulatorListener.class);
        }
     
        protected void fireNextStep() {
            for(SimulatorListener listener : getSimulatorListeners()) {
                listener.nextStep(this);
            }
        }
     
        protected void fireNumberOfAgentsChanged() {
            for(SimulatorListener listener : getSimulatorListeners()) {
                listener.numberOfAgentsChanged(agents.size());
            }
        }
     
        protected void fireNumberOfResourcesChanged() {
            for(SimulatorListener listener : getSimulatorListeners()) {
                listener.numberOfResourcesChanged(resources.size());
            }
        }
     
        protected void fireSomethingHappened(boolean canDo) {
            SomethingEvent event = null;
            for(SimulatorListener listener : getSimulatorListeners()) {
                if(event == null) {
                    event = new SomethingEvent(this, getSize(), canDo);
                }
                listener.somethingHappened(event);
            }
        }
    }
     
    ********************
     
    import java.util.EventListener;
     
    public interface SimulatorListener extends EventListener {
     
        void nextStep(Simulator source);
     
        void numberOfAgentsChanged(int numberOfAgents);
     
        void numberOfResourcesChanged(int numberOfResources);
     
        void somethingHappened(SomethingEvent event);
     
    }
     
    ********************
     
    public abstract class SimulatorAdapter {
     
        @Override
        public abstract void nextStep(Simulator source) {}
     
        @Override
        public abstract void numberOfAgentsChanged(int numberOfAgents) {}
     
        @Override
        public abstract void numberOfResourcesChanged(int numberOfResources) {}
     
        @Override
        public abstract void somethingHappened(SomethingEvent event) {}
     
    }
     
    ********************
    J'ai plus qu'à faire la génération des XEvent, mais bon c'est pas primordial donc je verrai plus tard. Mais c'est plus rapide comme ça

Discussions similaires

  1. Les design pattern pour créer des jeux
    Par alex6891 dans le forum Design Patterns
    Réponses: 4
    Dernier message: 26/11/2018, 19h59
  2. Réponses: 2
    Dernier message: 13/11/2005, 18h03
  3. Cherche tutoriel pour les Decision Cube
    Par megane dans le forum Bases de données
    Réponses: 4
    Dernier message: 27/02/2004, 13h05
  4. [Kylix] cherche composants pour les sockets
    Par coriolis dans le forum EDI
    Réponses: 1
    Dernier message: 09/04/2003, 10h18

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo