Łańcuch zobowiązań
Łańcuch zobowiązań (ang. Chain of responsibility, niekiedy tłumaczony także jako Łańcuch odpowiedzialności[1]) – czynnościowy wzorzec projektowy, w którym żądanie może być przetwarzane przez różne obiekty, w zależności od jego typu.
Problem
[edytuj | edytuj kod]Rozpatrzmy przykładowy komponent systemu, który przetwarza żądania przychodzące od innych komponentów. Każde żądanie ma określony typ, lub daje się zaklasyfikować do jakiejś kategorii. W zależności od typu, żądania mają być przetwarzane w odmienny sposób. Pragniemy zaimplementować mechanizm przetwarzania żądań, który umożliwiłby łatwe dodawanie w przyszłości obsługi nowych typów oraz ich usuwanie w razie potrzeby.
Budowa
[edytuj | edytuj kod]Wzorzec Łańcuch zobowiązań zakłada utworzenie oddzielnej klasy dla każdej procedury obsługi żądania dziedziczącej po pewnej klasie bazowej AbstrakcyjnaObsluga
. Obiekt każdej z procedur może posiadać wskazanie na następnik, tworząc w ten sposób łańcuch procedur przetwarzania. Aby przetworzyć żądanie, wykonujemy metodę operacja()
na pierwszym elemencie łańcucha. Jeśli nie potrafi on przetworzyć żądania, powinien przekazać je swojemu następnikowi:
public void operacja(żądanie: Żądanie) { jeśli potrafimy obsłużyć dany typ żądania żądanie: obsłuż żądanie w przeciwnym wypadku: przekaż żądanie następnikowi }
Konsekwencje użycia
[edytuj | edytuj kod]Zalety:
- elementy łańcucha mogą być dynamicznie dodawane i usuwane w trakcie działania programu[1],
- zmniejszenie liczby zależności między nadawcą a odbiorcami,
- implementacja pojedynczej procedury nie musi znać struktury łańcucha oraz innych procedur.
Wady:
- wzorzec nie gwarantuje, że każde żądanie zostanie obsłużone[1],
- śledzenie i debugowanie pracy działania łańcucha może być trudne[1].
Zastosowanie
[edytuj | edytuj kod]Wzorzec znajduje zastosowanie wszędzie tam, gdzie mamy do czynienia z różnymi mechanizmami podobnych żądań, które można zaklasyfikować do różnych kategorii. Dodatkową motywacją do jego użycia są często zmieniające się wymagania.
Przykład użycia
[edytuj | edytuj kod]Rozpatrzmy drzewo dokumentu (DOM). Każdy element może mieć elementy - dzieci, oraz ma swój element - rodzica w którym się zawiera (poza elementem głównym - rootem).
Użytkownik klika na któryś z elementów w drzewie. Jest to zarówno kliknięcie na ten element, jak i na elementy w których się znajduje ten element (rodziców - ponieważ znajdują się pod klikniętym elementem i kliknięcie może również dotyczyć ich). Elementy mogą mieć handler dla zdarzenia click - lub nie. Jeżeli obiekt posiada handler, obsługuje kliknięcie, i propagacja się kończy. Jeżeli handler uzna, że nie jest w stanie obsłużyć zdarzenia i zostanie wywołana odpowiednia metoda powodująca propagację wyżej, lub jeżeli obiekt nie posiada handlera, zdarzenie jest propagowane do rodzica klikniętego elementu. Cykl się powtarza dla rodzica - znowu, jeżeli potrafi on obsłużyć zdarzenie to propagacja się kończy, jeżeli nie, zdarzenie jest propagowane wyżej i wyżej, tak długo aż któryś z obiektów je obsłuży, lub propagacja osiągnie root element.
Zobacz też
[edytuj | edytuj kod]Przypisy
[edytuj | edytuj kod]- ↑ a b c d Eric Freeman, Elisabeth Freeman, Kathy Sierra, Bert Bates: Head First Design Patterns Edycja Polska. Helion, 2005, s. 634-635. ISBN 83-7361-792-2. (pol.).