Chain-of-responsibility pattern
The chain-of-responsibility pattern is a behavioral design pattern consisting of a source of command objects and a series of processing objects. Each processing object contains logic that defines the types of command objects that it can handle; the rest are passed to the next processing object in the chain. A mechanism also exists for adding new processing objects to the end of this chain.
In a variation of the standard chain-of-responsibility model, some handlers may act as dispatchers, capable of sending commands out in a variety of directions, forming a tree of responsibility. In some cases, this can occur recursively, with processing objects calling higher-up processing objects with commands that attempt to solve some smaller part of the problem; in this case recursion continues until the command is processed, or the entire tree has been explored. An XML interpreter might work in this manner.
This pattern promotes the idea of loose coupling.
What problems can the Chain of Responsibility design pattern solve?
- Coupling the sender of a request to its receiver should be avoided.
- It should be possible that more than one receiver can handle a request.
Implementing a request directly within the class that sends the request is inflexible because it couples the class to a particular receiver and makes it impossible to support multiple receivers
What solution does the Chain of Responsibility design pattern describe?
- Define a chain of receiver objects having the responsibility, depending on run-time conditions, to either handle a request or forward it to the next receiver on the chain (if any).
This enables us to send a request to a chain of receivers without having to know which one handles the request. The request gets passed along the chain until a receiver handles the request. The sender of a request is no longer coupled to a particular receiver.
abstract class HomeChecker {
protected $successor;
public abstract function check(HomeStatus $home);
public function succeedWith(HomeChecker $successor) {
$this->successor = $successor;
}
public function next(HomeStatus $home) {
if(!this->successor) {
$this->successor = check($home);
}
}
}
class Locks extends HomeChecker {
public function check(HomeStatus $home){
if(!home->locked) {
throw new Exception("The doors are not locked!");
}
$this->next($home);
}
}
class Lights extends HomeChecker {
public function check(HomeStatus $home){
if(!home->lightsOff) {
throw new Exception("The lights are on!");
}
$this->next($home);
}
}
class Alarm extends HomeChecker {
public function check(HomeStatus $home){
if(!home->alarmOn) {
throw new Exception("The alarm is not on!");
}
$this->next($home);
}
}
class HomeStatus {
public $alarmOn = false;
public $locked = true;
public $lightsOff = false;
}
$locks = new Locks;
$lights = new Lights;
$alarm = new Alarm;
$locks->succeedWith($lights);
$lights->succeedWith($alarm);
$locks->check(new HomeStatus);
More details can be found on my Github profile here.