Callback / Command vs EventListener / Observer Pattern

Próbuję zaprojektować framework asynchroniczny i chciałem wiedzieć, co ludzie uważają za plusy / minusy wzorca zwrotnego vs wzorca obserwatora.

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);
  }
}

Pracuję z frameworkiem, który zdaje się używać obu tych wzorców. Wzorzec EventListener nie jest typowym wzorcem, ponieważ nie ma listy słuchaczy. Można to łatwo zaimplementować, tworząc CompositeListener, który ma własną semantykę dotyczącą priorytetu słuchaczy i sposobu obsługi dystrybucji o zdarzeniach dla każdego słuchacza np. wywołanie nowego wątku dla każdego słuchacza vs powiadomienia seryjne. (Uważam, że jest to dobry pomysł, ponieważ jest to dobry rozdział obaw i jest poprawa w stosunku do standardowego wzorca obserwatora / słuchacza).

Jakieś pomysły na to, kiedy należy użyć każdego z nich?

Thxs.

Author: DD., 2012-01-21

4 answers

Oba wzorce są świetne i który z nich wybrać zależy od tego, co zamierzasz zbudować i jak Twój framework będzie używany.

Jeśli próbujesz zbudować jakiś system publikowania-subskrybowania z następującym typowym przepływem pracy:

  • Klient uruchamia zadanie asynchroniczne i zapomina o nim
  • wiele programów obsługi otrzymuje powiadomienia po zakończeniu zadania

Wtedy Observer wzór jest dla Ciebie naturalnym wyborem. Jak robisz framework powinieneś również rozważ użycie wzorca EventBus w celu uzyskania luźnego sprzężenia.

Jeśli potrzebujesz tylko prostej asynchronicznej realizacji i typowego przepływu używanego przez twój framework to:

  • Uruchom zadanie asynchroniczne
  • zrób coś po zakończeniu

Lub

  • Uruchom zadanie asynchroniczne
  • zrób coś
  • poczekaj aż to się skończy i zrób coś

Następnie należy wybrać proste Callback.

Ale w celu osiągnij bardziej użyteczne i czyste API polecam pozbyć się Callback abstrakcji i zaprojektować swój kod roboczy, aby zwrócił jakiś rodzaj Future.

public interface Worker<T> {

    Future<T> doAsync();

}

I Worker mogą być użyte w następujący sposób:

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

// some work here

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

Future może być standardem java Future . Ale sugerowałbym użycie ListenableFuture z biblioteki guava.

 22
Author: Mairbek Khadikov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-22 12:40:03

Wzorce polecenia, wywołania zwrotnego i obserwatora mają inną semantykę:

  • callback - powiadamia pojedynczego rozmówcę, że jakaś operacja zakończyła się jakimś wynikiem
  • observer - informuje zero do n zainteresowanych, że wydarzyło się jakieś zdarzenie (np. skończona operacja)
  • command - enkapsuluje wywołanie operacji w obiekcie, dzięki czemu można je przenosić przez przewód lub persist-able

W twoim przykładzie możesz połączyć oba wzorce callback i observer w celu uzyskania większej elastyczności API:

  1. Użyj wzorca wywołania zwrotnego do wyzwalania operacji i powiadomienia wywołującego asynchronicznie, że wywołana operacja została zakończona.
  2. Użyj wzorca event/observer , aby dać innym komponentom (które nie wywołały operacji , a nie {27]}) szansę na powiadomienie po zakończeniu operacji.
 28
Author: saintedlama,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-07 15:50:11

Argumentowałbym, że wzór wywołania zwrotnego jest lepszy, ponieważ jest prostszy, co oznacza, że będzie bardziej przewidywalny i mniej podatny na błędy ze względu na własny stan mutacji. Przykładem takiego działania może być sposób, w jaki GWT obsługuje komunikację między przeglądarką a serwerem .

Możesz jednak użyć leków generycznych:
//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
Author: Tom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-21 07:14:23

Oba wzorce mają kilka wspólnych intencji z wyjątkiem

Obserwowalny wzorzec może powiadomić wielu słuchaczy. Z drugiej strony, wzorzec polecenia będzie najlepiej dopasowany, gdzie potrzebujesz pojedynczego obsługi wywołania zwrotnego.

We wzorze poleceń można łatwo zaimplementować operację cofania.

Zdrówko!
 0
Author: dheeran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-08 06:46:44