Design patterns: Chain of Responsibility
The Chain of Responsibility Pattern is a behavioral design pattern that lets multiple objects handle a request without the sender knowing which specific object will process it. The objects are linked in a chain, and each object either handles the request or passes it on to the next one in the chain. This pattern decouples issuing a request from handling it, making it more flexible to wire components together and allowing chains to be changed without touching the client code.
Strengths
- No more complex conditionals: It avoids nested, complex conditional structures (
if-elseorswitch), improving code readability and maintainability. - Flexibility in defining the handling sequence: Handlers can be combined flexibly and dynamically, making it easy to change the order in which requests are processed without modifying the client logic.
- Separation between sender and receiver: The sender of the request doesn’t know the final handler, and handling can be delegated along the chain, encouraging adherence to the single responsibility principle.
- Incremental changes: It’s easy to add or remove handlers without modifying existing code, making the system easy to extend and maintain.
- Component reuse: Handlers can be reused in other chains or contexts, encouraging code reuse and reducing duplication.
Use Case
Suppose we want to implement a handling chain for a technical support request process, where different support tiers (for example, basic level, advanced level and administrator) handle requests based on their complexity.
PHP Code
// Handler interface
interface SupportHandler {
public function setNext(SupportHandler $handler): SupportHandler;
public function handle(string $issue): void;
}
// Concrete Handler for Basic Support
class BasicSupportHandler implements SupportHandler {
private $nextHandler;
public function setNext(SupportHandler $handler): SupportHandler {
$this->nextHandler = $handler;
return $handler;
}
public function handle(string $issue): void {
if ($issue === "basic") {
echo "Basic support is handling the issue.\n";
} elseif ($this->nextHandler) {
$this->nextHandler->handle($issue);
}
}
}
// Concrete Handler for Advanced Support
class AdvancedSupportHandler implements SupportHandler {
private $nextHandler;
public function setNext(SupportHandler $handler): SupportHandler {
$this->nextHandler = $handler;
return $handler;
}
public function handle(string $issue): void {
if ($issue === "advanced") {
echo "Advanced support is handling the issue.\n";
} elseif ($this->nextHandler) {
$this->nextHandler->handle($issue);
}
}
}
// Concrete Handler for Admin Support
class AdminSupportHandler implements SupportHandler {
private $nextHandler;
public function setNext(SupportHandler $handler): SupportHandler {
$this->nextHandler = $handler;
return $handler;
}
public function handle(string $issue): void {
if ($issue === "admin") {
echo "Admin support is handling the issue.\n";
} elseif ($this->nextHandler) {
$this->nextHandler->handle($issue);
}
}
}
// Usage
$basicHandler = new BasicSupportHandler();
$advancedHandler = new AdvancedSupportHandler();
$adminHandler = new AdminSupportHandler();
// Building the chain of responsibility
$basicHandler->setNext($advancedHandler)->setNext($adminHandler);
// Issue a request
echo "Issue: basic\n";
$basicHandler->handle("basic");
echo "\nIssue: advanced\n";
$basicHandler->handle("advanced");
echo "\nIssue: admin\n";
$basicHandler->handle("admin");
echo "\nIssue: unknown\n";
$basicHandler->handle("unknown");
Explanation
- BasicSupportHandler, AdvancedSupportHandler and AdminSupportHandler are the different handlers that deal with the various levels of support issues.
- Each handler decides whether it can handle the request or passes it on to the next one in the chain, which is set via the setNext() method.
- If no handler deals with a request, it is simply ignored, or you can add a fallback.
Conclusion
The Chain of Responsibility Pattern is ideal for scenarios where requests need to be processed flexibly by a series of potentially independent handlers. It makes the system more extensible and easier to maintain, reducing code complexity and improving readability.