chain of responsibility

Łańcuch odpowiedzialności to wzorzec projektowy, który znajduje zastosowanie w sytuacji, gdy konkretne żądanie może zostać obsłużone przez różne obiekty. Tworzą one uporządkowaną strukturę przypominającą swoją budową łańcuch. Jeśli jeden z nich nie jest w stanie prawidłowo zająć się request’em, to przekazuje jego obsługę dalej, do kolejnego handler’a. Na końcu zazwyczaj umieszczane jest działanie domyślne. Cały proces zobrazowano na poniższym rysunku:

chain

Gdzie:

  • request – żądanie
  • A,B,C – obiekty tworzące łańcuch
  • Default – obiekt odpowiadający za zachowanie domyślne, stąd też znajduje się na końcu

Zatem żądanie może zakończyć swoją „wędrówkę” już na obiekcie A, ale równie dobrze może dotrzeć do samego końca hierarchii.

Gdybyśmy mieli odnieść się do sytuacji wziętej z życia, to znakomitym przykładem wydaje się być obieg pisma pomiędzy wydziałami dużej organizacji. Po zapoznaniu się z dokumentem konkretna filia podejmuje decyzję o jego rozpatrzeniu lub o przekazaniu do innego oddziału. Koniec końców dokument trafi na odpowiednie biurko.

prosty przykład praktyczny

Przyjmijmy założenie, iż w zależności od typu zmiennej będącej żądaniem, jego obsługa będzie delegowana do dedykowanego obiektu. Na początku potrzebny nam jest abstrakcyjny zarys dla klas stanowiących elementy łańcucha:

abstract class Handler {

	protected $nextHandler = NULL;

	// zajmuje sie obsluga requesta
	abstract public function Request ($request);

	// setter dla kolejnego handler'a
	public function setNextHandler(Handler $h) {

		$this->nextHandler = $h;

	}

}

Teraz ich definicja:

// obsluga dla zmiennej typu boolean
class Boolean extends Handler {

	public function Request ($request) {

		if (is_bool($request))
			return "BOOL";
		else
			if ($this->nextHandler instanceof Handler)
				return $this->nextHandler->Request($request);

	}

}

// obsluga dla zmiennej typu numeric
class Numeric extends Handler {

	public function Request ($request) {

		if (is_numeric($request))
			return "NUMERIC";
		else
			if ($this->nextHandler instanceof Handler)
				return $this->nextHandler->Request($request);

	}

}

// obsluga dla zmiennej typu string
class String extends Handler {

	public function Request ($request) {

		if (is_string($request))
			return "STRING";
		else
			if ($this->nextHandler instanceof Handler)
				return $this->nextHandler->Request($request);

	}

}

// obsluga zachowania domyslnego - na koncu lancucha
class DefaultHandler extends Handler {

	public function Request ($request) {

		return "DON'T KNOW";

	}

}

Na koniec klient:

// klient
class Client {

	public function request ($request) {

		// tworzymy instancje elementow lancucha
		$string = new String();
		$numeric = new Numeric();
		$boolean = new Boolean();
		$default = new DefaultHandler();

		// ustawiamy kolejnosc elementow w lancuchu
		// string > numeric > boolean > default
		$string->setNextHandler($numeric);
		$numeric->setNextHandler($boolean);
		$boolean->setNextHandler($default);
		return $string->Request($request);

	}

}

// przykladowe uzycie
$client = new Client();
var_dump($client->request(array(1)));

Wśród zalet chain of responsibility należy wymienić możliwość dowolnego formowania przebiegu obsługi request’a. W dowolnej chwili można utworzyć nowe elementy lub usunąć istniejące. Zachęcam do głębszego zapoznania się z tym wzorcem projektowym.

Pozostaw Odpowiedź