Wydarzenie jest zgłoszenie opisując zdarzenie z niedawnej przeszłości.
Typowa implementacja systemu sterowanego zdarzeniami wykorzystuje funkcje dyspozytora zdarzeń i funkcji obsługi (lub subskrybentów ). Dyspozytor udostępnia interfejs API do obsługi procedur obsługi zdarzeń (jQuery's bind
) oraz metodę publikowania zdarzenia swoim subskrybentom ( trigger
w jQuery). Gdy mówisz o zdarzeniach we / wy lub interfejsu użytkownika, zwykle występuje również pętla zdarzeń , która wykrywa nowe zdarzenia, takie jak kliknięcia myszą i przekazuje je do dyspozytora. W JS-land dyspozytor i pętla zdarzeń są dostarczane przez przeglądarkę.
W przypadku kodu, który współdziała bezpośrednio z użytkownikiem - reagując na naciśnięcia klawiszy i kliknięcia - programowanie sterowane zdarzeniami (lub jego odmiana, taka jak programowanie reaktywne ) jest prawie nieuniknione. Ty, programista, nie masz pojęcia, kiedy i gdzie użytkownik kliknie, więc to do GUI lub przeglądarki należy wykrycie akcji użytkownika w pętli zdarzeń i powiadomienie o kodzie. Tego typu infrastruktura jest również wykorzystywana w aplikacjach sieciowych (por. NodeJS).
Twój przykład, w którym wywołujesz zdarzenie w kodzie zamiast wywoływać funkcję bezpośrednio, ma kilka ciekawszych kompromisów, które omówię poniżej. Główną różnicą jest to, że wydawca zdarzenia ( makeItSnow
) nie określa odbiorcy wywołania; jest to połączone gdzie indziej (w wywołaniu do bind
w twoim przykładzie). Nazywa się to ogniem i zapomnieniem : makeItSnow
ogłasza światu, że pada śnieg, ale nie obchodzi go, kto słucha, co będzie dalej lub kiedy to nastąpi - po prostu transmituje wiadomość i odkurza ręce.
Podejście oparte na zdarzeniach rozdziela nadawcę wiadomości od odbiorcy. Jedną z zalet, jakie to daje, jest to, że dane zdarzenie może mieć wielu procedur obsługi. Możesz powiązać gritRoads
funkcję ze zdarzeniem śniegowym bez wpływu na istniejący shovelSnow
moduł obsługi. Masz elastyczność w sposobie podłączenia aplikacji; Aby wyłączyć zachowanie, wystarczy usunąć bind
połączenie, a nie poszukać kodu, aby znaleźć wszystkie wystąpienia tego zachowania.
Kolejną zaletą programowania opartego na zdarzeniach jest to, że daje on możliwość postawienia problemów przekrojowych. Dyspozytor zdarzeń pełni rolę Mediatora , a niektóre biblioteki (takie jak Brighter ) wykorzystują potok, dzięki czemu można łatwo podłączyć ogólne wymagania, takie jak rejestrowanie lub jakość usług.
Pełne ujawnienie: Jaśniejszy został opracowany w Huddle, gdzie pracuję.
Trzecią zaletą oddzielenia nadawcy zdarzenia od odbiorcy jest to, że zapewnia on elastyczność podczas obsługi zdarzenia. Możesz przetwarzać każdy typ zdarzenia we własnym wątku (jeśli obsługuje go program wysyłający zdarzenia) lub możesz umieścić wywołane zdarzenia w brokerze komunikatów, takim jak RabbitMQ, i obsłużyć je za pomocą procesu asynchronicznego, a nawet przetworzyć je zbiorczo przez noc. Odbiorca zdarzenia może być w osobnym procesie lub na osobnej maszynie. Aby to zrobić, nie musisz zmieniać kodu wywołującego zdarzenie! Jest to wielka idea związana z architekturami „mikrousług”: usługi autonomiczne komunikują się za pomocą zdarzeń, a oprogramowanie pośredniczące w komunikacji stanowi kręgosłup aplikacji.
Aby uzyskać raczej inny przykład stylu opartego na zdarzeniach, spójrz na projekt oparty na domenie, w którym zdarzenia w domenie służą do oddzielenia agregatów. Rozważmy na przykład sklep internetowy, który poleca produkty na podstawie historii zakupów. Customer
Musi mieć swoją historię zakupów aktualizowany, gdy ShoppingCart
jest opłacone. ShoppingCart
Kruszywo może powiadomić Customer
o podnoszenie CheckoutCompleted
zdarzenie; Customer
będą aktualizowane w oddzielnej transakcji w odpowiedzi na zdarzenie.
Główną wadą tego modelu opartego na zdarzeniach jest pośrednictwo. Teraz trudniej jest znaleźć kod, który obsługuje zdarzenie, ponieważ nie można po prostu nawigować do niego za pomocą IDE; musisz dowiedzieć się, gdzie zdarzenie jest powiązane w konfiguracji i mieć nadzieję, że znalazłeś wszystkie procedury obsługi. W twojej głowie jest więcej rzeczy do zapamiętania. Pomocne mogą być tutaj konwencje stylu kodu (na przykład umieszczenie wszystkich wywołań bind
w jednym pliku). Ze względu na twoje zdrowie psychiczne ważne jest, aby używać tylko jednego dyspozytora zdarzeń i konsekwentnie go używać.
Kolejną wadą jest to, że trudno jest refaktoryzować zdarzenia. Jeśli chcesz zmienić format zdarzenia, musisz także zmienić wszystkie odbiorniki. Zaostrza się to, gdy subskrybenci wydarzenia znajdują się na różnych komputerach, ponieważ teraz musisz zsynchronizować wersje oprogramowania!
W niektórych okolicznościach wydajność może stanowić problem. Podczas przetwarzania wiadomości dyspozytor musi:
- Wyszukaj poprawne procedury obsługi w jakiejś strukturze danych.
- Zbuduj potok przetwarzania komunikatów dla każdego modułu obsługi. Może to obejmować wiele przydziałów pamięci.
- Wywoływaj dynamicznie programy obsługi (ewentualnie używając refleksji, jeśli język tego wymaga).
Jest to z pewnością wolniejsze niż zwykłe wywołanie funkcji, które polega tylko na pchnięciu nowej ramki na stosie. Jednak elastyczność, jaką zapewnia architektura oparta na zdarzeniach, znacznie ułatwia izolowanie i optymalizację wolnego kodu. Możliwość przesłania pracy do procesora asynchronicznego jest tutaj dużą wygraną, ponieważ pozwala natychmiast obsłużyć żądanie, podczas gdy ciężka praca jest wykonywana w tle. W każdym razie, jeśli wchodzisz w interakcję z DB lub rysujesz rzeczy na ekranie, koszty IO całkowicie zwiększą koszty przetwarzania wiadomości. Jest to przypadek uniknięcia przedwczesnej optymalizacji.
Podsumowując, wydarzenia to świetny sposób na tworzenie luźno sprzężonego oprogramowania, ale nie są one bezpłatne. Błędem byłoby na przykład zastąpienie każdego wywołania funkcji w aplikacji zdarzeniem. Wykorzystuj wydarzenia, aby tworzyć znaczące podziały architektoniczne.
$(document).bind('snow', shovelShow)
. Nie ma potrzeby zawijania go w anonimowej funkcji.