web-dev-qa-db-fra.com

Java. Modèle correct pour la mise en œuvre d'auditeurs

Très typiquement, j'ai une situation où un objet donné devra avoir de nombreux auditeurs. Par exemple, je pourrais avoir

class Elephant {
  public void addListener( ElephantListener listener ) { ... }
}

mais j'aurai beaucoup de telles situations. C'est-à-dire que je vais également avoir un objet Tiger qui aura TigerListeners. Maintenant, TigerListeners et ElephantListeners sont assez différents:

interface TigerListener {
  void listenForGrowl( Growl qrowl );
  void listenForMeow( Meow meow );
}

tandis que

interface ElephantListener {
  void listenForStomp( String location, double intensity );
}

Je trouve que je dois toujours continuer à rééchaler le mécanisme de radiodiffusion dans chaque classe d'animaux, et la mise en œuvre est toujours la même. Y a-t-il un modèle préféré?

26
Jake

Au lieu de chaque Listener ayant des méthodes spécifiques pour chaque type d'événement que vous pouvez l'envoyer, modifiez l'interface pour accepter une classe générique Event. Vous pouvez ensuite sous-classes Event à des sous-types spécifiques si vous avez besoin ou si vous le souhaitez contenir d'état tel que double intensity.

Tigerlistener et Elephentlistener sont alors devenus

interface TigerListener {
    void listen(Event event);
}

En fait, vous pouvez ensuite refacturer davantage cette interface dans une plaine Listener:

interface Listener {
    void listen(Event event);
}

Vos implémentations Listener peuvent ensuite contenir la logique dont elles ont besoin pour les événements spécifiques qui se soucient de

class TigerListener implements Listener {
    @Overrides
    void listen(Event event) {
        if (event instanceof GrowlEvent) {
            //handle growl...
        }
        else if (event instance of MeowEvent) {
            //handle meow
        }
        //we don't care about any other types of Events
    }
}

class ElephentListener {
    @Overrides
    void listen(Event event) {
        if (event instanceof StompEvent) {
            StompEvent stomp = (StompEvent) event;
            if ("north".equals(stomp.getLocation()) && stomp.getDistance() > 10) { 
                ... 
            }
        }
    }
}

La relation clé entre l'abonné et l'éditeur est que l'éditeur peut envoyer des événements aux abonnés, ce n'est pas nécessairement qu'il peut envoyer certains types d'événements - ce type de refactoring pousse cette logique de l'interface dans les implémentations spécifiques dans les implémentations spécifiques. .

28
matt b

Je pense que vous le faites correctement, car vos interfaces ont une valeur sémantique et expriment ce qu'ils écoutent (par exemple, grognements et mières au lieu de piétine). Avec une approche générique, vous pourrez peut-être réutiliser le code de la radiodiffusion, mais vous risquez de perdre la lisibilité.

Par exemple, il y a le Java.beans.PropertyChangeSupport Qui est un utilitaire permettant d'exécuter des oberservers à écouter des changements de valeur. Il fait la radiodiffusion, mais vous devez toujours mettre en œuvre la méthode de votre classe de domaine et déléguer à l'objet de propriétéChangeSupport. Les méthodes de rappel n'ont-elles pas de sens en eux-mêmes et les événements diffusés sont basés sur des cordes:

public interface PropertyChangeListener extends Java.util.EventListener {
     void propertyChange(PropertyChangeEvent evt);
}

Un autre est Java.util.Observable Qui fournit le mécanisme de radiodiffusion, mais ce n'est pas non plus la meilleure chose à imho.

J'aime ElephantListener.onStomp()

3
mhaller

Une autre option est le modèle de tableau blanc . Cela déconnecte l'éditeur et les abonnés les uns des autres et ne contiendra aucun code de radiodiffusion. Ils utilisent tous les deux simplement un mécanisme de messagerie pour pub/sous et ni connexion directe à l'autre.

Ceci est un modèle commun pour la messagerie dans une plate-forme OSGI.

1
Robin