Łańcuch zobowiązań

Z Wikipedii, wolnej encyklopedii

Ł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]

Diagram klas wzorca Łańcuch zobowiązań

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]

  1. 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.).

Linki zewnętrzne[edytuj | edytuj kod]