web-dev-qa-db-fra.com

Rappel/Commande vs EventListener/Observer Pattern

J'essaie de concevoir un cadre asynchrone et je voulais savoir ce que les gens pensent être le pour/le contre du motif de rappel par rapport au motif d'observateur.

Callback pattern:

//example callback
public interface Callback{
    public void notify(MethodResult result);
}

//example method
public class Worker{
  public void doAsyncWork(Callback callback){
     //do work
     callback.notify(result);
  }
}

//example observer pattern
public interface EventListener{
   public void notify(MethodResult result);

}

public class Worker{
  private EventListener listener;
  public registerEventListener(EventListener listener){
   this.listener=listener;
  }
  public void doAsyncWork(){
     //do work
     listener.notify(result);
  }
}

Je travaille avec un cadre qui semble utiliser ces deux modèles. Le modèle EventListener n'est pas le modèle typique car il ne contient pas de liste des écouteurs. Ceci peut facilement être implémenté en créant un CompositeListener qui a sa propre sémantique sur la priorité des écouteurs et sur la façon de gérer la distribution des événements à chaque auditeur, par exemple création d'un nouveau thread pour chaque écouteur par rapport aux notifications en série. (Je pense en fait que c’est une bonne idée car c’est une bonne séparation des préoccupations et une amélioration du modèle standard observateur/auditeur).

Avez-vous des idées sur le moment d'utilisation de chacun? 

Thxs.

29
DD.

Les deux modèles sont excellents et le choix dépend de ce que vous allez construire et de la manière dont votre framework sera utilisé.

Si vous essayez de créer un système de publication-abonnement avec le flux de travail typique suivant:

  • le client commence la tâche asynchrone et l'oublie
  • plusieurs gestionnaires reçoivent des notifications lorsque la tâche est terminée 

alors le motif Observer est un choix naturel pour vous. Lorsque vous utilisez un framework, vous devez également envisager l’utilisation de EventBus pattern pour obtenir un couplage lâche.

Si vous n'avez besoin de rien de plus qu'une simple exécution asynchrone et qu'un flux typique utilisant votre infrastructure est:

  • démarrer la tâche async
  • faire quelque chose quand c'est terminé

ou

  • démarrer la tâche async
  • faire quelque chose
  • attendez qu'il soit terminé et faites quelque chose

alors vous devriez aller avec Callback simple.

Mais pour obtenir une API plus propre et utilisable, je vous recommanderais de vous débarrasser de l'abstraction Callback et de concevoir votre code opérateur pour renvoyer une sorte de Future.

public interface Worker<T> {

    Future<T> doAsync();

}

Et Worker peut être utilisé de la manière suivante:

Future<Integer> future = worker.doAsync();

// some work here

Integer result = future.get(); // waits till async work is done

Future pourrait être un standard Java Future . Mais je vous suggérerais d'utiliser ListenableFuture from gava library.

23
Mairbek Khadikov

Les modèles de commande, de rappel et d'observateur ont une sémantique différente:

  • callback - notifie à un appelant qu'une opération s'est terminée avec un résultat
  • observer - notifie de zéro à n parties intéressées qu'un événement (par exemple une opération terminée) s'est produit
  • commande - encapsule un appel d'opération dans un objet, le rendant ainsi transférable sur un fil ou permanent

Dans votre exemple, vous pouvez combiner des modèles de rappel et d'observateur pour obtenir une plus grande flexibilité d'API:

  1. Utilisez le modèle callback pour déclencher des opérations et informez l'appelant de manière asynchrone que l'opération déclenchée s'est terminée.
  2. Utilisez le modèle event/observer pour donner à certains autres composants (qui non n'a pas déclenché l'opération) la possibilité d'être averti à la fin d'une opération.
30
saintedlama

Je dirais que le modèle de rappel est meilleur car il est plus simple, ce qui signifie qu'il sera plus prévisible et moins susceptible d'avoir des bogues en raison de son propre état de mutation. Un exemple de ceci en fonctionnement serait la façon dont GWT gère la communication navigateur/serveur .

Vous voudrez peut-être utiliser des génériques:

//example callback
public interface Callback<T> {
    public void notify(T result);
}

//example method
public class Worker{
  public void doAsyncWork(Callback<SomeTypeOrOther> callback){
     //do work
     callback.notify(result);
  }
}
5
Tom

Les deux modèles partagent peu d'intentions communes excepté,

Le modèle observable peut notifier plusieurs auditeurs. D'autre part, le modèle de commande sera le mieux adapté, où vous avez besoin d'un gestionnaire de rappel unique.

Dans le modèle de commande, il est facile d'implémenter l'opération d'annulation.

À votre santé!

0
dheeran