2010-11-09 6 views
1

J'ai actuellement une configuration Java Observer/Observable dans laquelle j'allume un champ dans le paramètre Object de Observer.update (par exemple ID d'événement) pour déterminer comment gérer une notification Observable.Conception de l'observateur - comment accéder à la méthode method.invoke?

cela crée un code bavard comme:

public void update (Observable o, Object arg) { 
    if (arg instanceof Event) { 
     switch (((Event)arg).getID()) { 
      case EVENT_TYPE_A: 
       // do stuff... 
       break; 
      case EVENT_TYPE_B: 
       // do stuff... 
       break; 
      case EVENT_TYPE_C: 
       // do stuff... 
       break; 
     } 
    } 
} 

provenant d'un arrière-plan ActionScript, cela se sent inutilement bavard me ... au lieu de passer une instance d'un observateur, je préfère passer une méthode de rappel être appelé directement par l'observable (plus précisément, par une sous-classe). Cependant, je ne sais pas comment déterminer l'objet sur lequel la méthode devrait être appelée (l'instance de classe qui 'possède' la méthode).

Je pourrais passer une référence à l'instance qui contient la méthode, mais cela sent mauvais OOP.

Est-ce que j'aboie le mauvais arbre? ou y a-t-il un moyen propre d'y parvenir?

+0

Je vois votre dilemme; ce n'est pas le modèle d'observateur. Voir http://stackoverflow.com/questions/5423125/polymorphism-and-interfaces-in-java-can-polymorphism-be-used-to-implement-interf/5423860#5423860 –

Répondre

1

Une implémentation plus propre impliquerait de supprimer la logique consistant à savoir si l'événement peut être géré par l'observateur/observable, à l'observateur réel/observable lui-même. Il semble que ActionScript vous ait laissé une drôle d'idée sur le pattern Observer. Observer (sans jeu de mots intentionnée):

public interface Observer{ 

    public void update(Event arg); 
} 

public class Event{ 

    public int ID; 
} 

public Button implements Observer{ 

    public void update (Event arg){ 

    switch (arg.ID){ 

     case 1: //Buttonsy handle events of type 1 
     //do something useful; 
     break; 
     default: 
     System.out.println("Buttons don't handle events of ID: " + arg.ID); 
     break; 
    } 
    } 
} 

public ProgressBar implements Observer{ 

    public void update (Event arg){ 

    switch (arg.ID){ 

     case 2: //ProgressBars handle events of type 2 and 3 
     //do something useful; 
     break; 
     case 3: 
     //do something else useful; 
     break; 
     default: 
     System.out.println("Progress bars don't handle events of ID: " + arg.ID); 
     break; 
    } 
    } 
} 


public class Subject{ 

private ArrayList<Observer> allUIControls; 

public registerControl(Observer control){ 

    allUIControls.add(control); 
} 

public void updateControls (Event arg) { 

    foreach (Observer control in allUIControls){ 

    //pass the event to each UI control, and let the EVENT decide if it can understand the Event.ID 
    //its not the job of Subject to decide if the Observer is fit to handle the event. THIS IS NOT THE OBSERVER pattern. 
    control.update(arg); 
    } 
} 
} 
+0

ouais, c'est là où je suis maintenant. Je peux vivre avec les commutateurs dans les gestionnaires d'événements des observateurs. c'est juste un paradigme quelque peu différent de celui d'ActionScript; je vois les avantages et les inconvénients de chacun. – ericsoco

2

Cela peut être un peu loin dans le champ gauche, mais depuis Java 5 et plus ont des génériques, à la fois l'observateur et motifs traditionnels d'écoute semblent un peu démodées. C'est-à-dire, les types sont le lingua-fraca de Java ces jours-ci. Les événements avec des ID entiers existent principalement parce que les instructions switch contre les constantes sont extrêmement efficaces - au détriment de la lisibilité et nécessitant souvent des castings pour faire quelque chose d'utile (vous pouvez savoir que l'ID = 23, l'Object doit être un MouseEvent et plus sûr si vous laissez le compilateur et les informations de type d'exécution prendre soin de cela pour vous). Sur une machine moderne dans une JVM moderne, l'efficacité peut ne pas en valoir la peine.

Donc, si vous n'êtes pas marié aux ID et le modèle d'observation traditionnel, vous pourriez envisager quelque chose comme ceci:

public abstract class Observer<T> { 
    private final Class<T> type; 

    protected Observer(Class<T> type) { 
    this.type = type; 
    } 

    //implement this method; if it returns false, the event (object) 
    //is "consumed" and no other observers should be called 
    public abstract boolean onEvent(T instance); 

    protected final boolean matches(Object obj) { 
    return type.isInstance(obj); 
    } 

    Boolean maybeDispatch(Object o) { 
    if (matches(o)) { 
     return onEvent(type.cast(o)); 
    } 
    return null; 
    } 
} 

Cela nous amène (littéralement) un observateur générique d'événements; nous allumons le type de l'objet passé dans comme ceci:

public class Bus { 
    private final List<Observer<?>> observers = new ArrayList<Observer<?>>(); 

    public void registerObserver(Observer<?> observer) { 
    observers.add(observer); 
    } 

    public <T> void onEvent(T t) { 
    Boolean keepGoing; 
    for (Observer<?> obs : observers) { 
     keepGoing = obs.maybeDispatch(t); 
     if (keepGoing != null && !keepGoing.booleanValue()) { 
     break; 
     } 
    } 
    } 
} 

Le code résultant est (légèrement) moins efficace, mais écrire une sous-classe d'un tel « observateur » est infiniment plus facile à lire. Il ne ressemble pas beaucoup au modèle d'observateur traditionnel, mais est fonctionnellement équivalent.

Si vous avez encore besoin d'un paramètre "événement" supplémentaire, vous pouvez simplement faire une logique similaire pour paramétrer deux types.

+0

hm, très intéressant ... pour l'instant, je voudrais rester avec Java Observer, mais cela semble être un bon candidat pour re-architecturer plus tard. Je suis encore un peu un java noob (au cas où vous ne l'auriez pas deviné) et les génériques sont nouveaux pour moi, je les ai utilisés avec le framework Collections. mais je commence à avoir une idée de la flexibilité qu'ils apportent, tout en maintenant la sécurité du type et la bonté de compilation ... – ericsoco

Questions connexes