28

Próbuję zaprojektować strukturę asynchroniczną i chciałbym wiedzieć, co ludzie uważają za plusy i minusy wzoru wywołania zwrotnego w porównaniu do wzorca obserwatora.Callback/Command vs. EventListener/Observer Pattern

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ę w ramach, który wydaje się korzystać z obu tych wzorów. Wzorzec EventListener nie jest typowym wzorcem, ponieważ nie ma listy detektorów. Można to jednak łatwo zaimplementować, tworząc komponent CompositeListener, który ma własną semantykę dotyczącą priorytetu słuchaczy i sposób obsługi dystrybucji zdarzeń dla każdego detektora, np. spawnowanie nowego wątku dla każdego detektora w porównaniu do powiadomień seryjnych. (Właściwie uważam, że to dobry pomysł, ponieważ jest to dobry rozdział obaw i stanowi ulepszenie standardowego wzorca obserwatora/słuchacza).

Jakieś przemyślenia na temat tego, kiedy należy ich używać?

Thxs.

+0

Aby uprościć oddzwanianie, zalecamy korzystanie z funkcji pierwszej klasy. więc jest to po prostu 'public void doAsyncWork (funkcja oddzwaniania)' – Raynos

+0

Jestem nowy w obu wzorcach projektowych i bardzo się pomyliłem z próbkami kodu. Jesteś pewien, że to poprawne demo tych dwóch wzorów, szczególnie wzorca Obserwatora? Dla mnie sposób, w jaki je zapisałeś, nie ma znaczenia. Nie zrozum mnie źle. Szczerze doceniam twoje pytanie, ponieważ mam to na myśli. Po prostu chcesz to wyprostować. Dzięki! – jiu9x9uij

Odpowiedz

22

Oba wzory są świetne, a które z nich wybrać, zależy od tego, co masz zamiar zbudować iw jaki sposób zostaną wykorzystane ramy.

Jeśli próbujesz zbudować jakąś publikować subskrybowania systemu z następującym typowym przepływem pracy:

  • klient rozpoczyna zadanie asynchronicznej i zapomina o to
  • wiele koparki otrzymuje powiadomienia, kiedy zadanie zostanie zakończone

następnie Observer wzór jest naturalnym wyborem dla Ciebie. Podczas wykonywania struktury należy również rozważyć użycie wzoru EventBus, aby uzyskać luźne połączenie.

Jeśli potrzebujesz niczego więcej niż prostego asynchronicznego wykonywania i typowy przepływ używając swojego ram jest:

  • zadanie początek asynchroniczny
  • coś zrobić, gdy zostanie zakończone

lub

  • Rozpocznij asynchroniczne zadanie
  • zrobić coś
  • czekać, aż zostanie zakończona i zrobić coś

następnie należy udać się z prostego Callback.

Jednak w celu uzyskania bardziej użytecznego i czystego API, polecam pozbycie się abstrakcji Callback i zaprojektowanie kodu roboczy, aby zwrócić coś w rodzaju Future.

public interface Worker<T> { 

    Future<T> doAsync(); 

} 

I Worker można stosować następujący sposób:

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

// some work here 

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

Future mogła być standardowym java Future. Ale proponuję użyć ListenableFuture z biblioteki guawy.

5

Twierdzę, że wzorzec wywołania zwrotnego jest lepszy, ponieważ jest prostszy, co oznacza, że ​​będzie on bardziej przewidywalny i mniej prawdopodobne, że będzie miał błędy z powodu swojego własnego stanu mutacji. Przykładem tego może być the way GWT handles browser/server communication.

Możesz chcieć użyć rodzajowych choć:

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

poleceń oddzwonienia i obserwatorów wzory mają różne semantyki:

  • zwrotnych - powiadamia jednego rozmówcę, że niektóre Operacja zakończona z pewnym wyniku
  • obserwator - powiadamia zera do n zainteresowane strony, że niektóre zdarzenie (na przykład zakończona operacja) zdarzyło się
  • komenda - hermetyzuje wywołanie operacji w obiekcie, dzięki czemu można je przenosić przez drut lub utrzymują-stanie

W przykładzie można połączyć oba wzory zwrotne i obserwatorów w celu osiągnięcia większej elastyczności API:

  1. użyć deseniu zwrotna do uruchomienia operacji i powiadomić abonenta asynchronicznie że operacja wyzwolona została zakończona.
  2. Użyj zdarzeń/obserwatora wzór dać kilka innych elementów (które czynił nie spust operacja) szansę być powiadomiony, gdy operacja zakończy.
+1

Kluczowym punktem jest tutaj: - wykorzystanie wywołania zwrotnego do powiadamiania wyzwalacza - wykorzystanie zdarzeń przeciwpożarowych do powiadamiania obserwatorów, że w tym przypadku nie wywołałoby zdarzenia. – metakungfu

+1

Wyjaśnienie różnic między trzema wzorami jest dla mnie pomocne. – Sentimental