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.
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.
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:
- Użyj wzorca wywołania zwrotnego do wyzwalania operacji i powiadomienia wywołującego asynchronicznie, że wywołana operacja została zakończona.
- 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.
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);
}
}
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!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