Chain of responsibility

Przeznaczenie

  • Umożliwia uniknięcie związania wysyłającego żądanie z odbiorcą żądania przez danie więcej niż jednemu obiektowi szansy obsłużenia tego żądania

  • Żądanie jest przesyłane wzdłuż łańcucha obiektów, aż któryś je obsłuży

  • Pierwszy obiekt z łańcucha odbiera żądanie i albo je obsługuje albo przekazuje dalej do następnego kandydata w łańcuchu, który postępuje tak samo

  • Obiekt, który wygenerował żądanie, nie wie, kto je obsłuży – żądanie ma niejawnego odbiorcę

  • Aby przekazywać żądania wzdłuż łańcucha i zagwarantować, że odbiorcy pozostaną niejawni, każdy obiekt w łańcuchu korzysta ze wspólnego interfejsu obsługi żądań i uzyskiwania dostępu do następnika w łańcuchu

Kontekst

  • Więcej niż jeden obiekt może obsłużyć żądanie, a obiekt obsługujący nie jest znany a priori

  • Wykonanie żądania nie jest gwarantowane

  • Zbiór obiektów, które mogą obsłużyć żądanie, może być określony dynamicznie

Problem

  • Chcemy wysłać żądanie do jednego z kilku obiektów, nie określając jawnie odbiorcy

  • Chcemy odseparować nadawcę żądania od jego odbiorców

Struktura

_images/Chain.png

Uczestnicy

Handler

  • definiuje interfejs obsługi żądań

  • (nieobowiązkowo) implementuje odwołanie do następnika

Concrete Handler

  • obsługuje te żądania, za spełnienie których odpowiada

  • może mieć dostęp do swojego następnika

  • jeśli może obsłużyć żądanie, to robi to - w przeciwnym wypadku, przekazuje żądanie do swojego następnika

Client – generuje żądanie i przesyła je do obiektu klasy ConcreteHandler, występującego w łańcuchu

Konsekwencje

  1. Zredukowanie powiązań. Dany obiekt nie musi wiedzieć, który inny obiekt obsłuży żądanie.

  2. Dodatkowa elastyczność w przydzielaniu obiektom zobowiązań. Chain of Responsibility zwiększa elastyczność rozdzielania zobowiązań między obiekty.

  3. Brak gwarancji odebrania żądania. Ponieważ odbiorca żądania nie jest jawnie znany, nie ma gwarancji, że zostanie ono obsłużone – żądanie może wypaść z łańcucha, nie zostawszy w ogóle obsłużone.

Implementacja

  1. Implementacja łańcucha następników – dwa możliwe sposoby:

  • zdefiniowanie nowych powiązań

  • użycie istniejących powiązań

  1. Łączenie następników. Jeśli nie ma wcześniej określonych odwołań umożliwiających zdefiniowanie łańcucha, to trzeba je wprowadzić samemu. W tym wypadku obsługa nie tylko definiuje interfejs dla żądań, ale zwykle także utrzymuje następniki.

  2. Reprezentowanie żądań. Zakodowane na sztywno wywołanie operacji. Użycie jednej funkcji obsługującej, która jako argument dostaje kod żądania (np. stałą całkowitą lub napis). Do przekazywania parametrów można użyć oddzielnych obiektów-żądań, które zawierają parametry żądania. Klasa Żądanie może jawnie reprezentować żądania, a nowe rodzaje żądań można określać tworząc jej podklasy.

Wzorce pokrewne

Composite - kompozyt:

  • często stosowany w połączeniu z Chain of Responsibility,

  • rodzic komponentu może odgrywać rolę jego następnika.