Design patterns: Chain of Responsability
Il Chain of Responsibility Pattern è un design pattern comportamentale che permette a più oggetti di gestire una richiesta senza che il mittente sappia quale oggetto specifico la elaborerà. Gli oggetti sono collegati in una catena e ogni oggetto gestisce la richiesta o la passa al successivo nella catena. Questo pattern separa l’emissione di una richiesta dalla sua gestione, aumentando la flessibilità nel collegare componenti e consentendo la modifica delle catene senza cambiare il codice client.
Punti di Forza
- Eliminazione di Condizioni Complesse: Evita l’uso di strutture condizionali annidate e complesse (
if-else
oswitch
), migliorando la leggibilità e la manutenibilità del codice. - Flessibilità nel Definire la Sequenza di Gestione: Gli handler possono essere combinati in modo flessibile e dinamico, cambiando facilmente la sequenza in cui vengono processate le richieste senza modificare la logica del client.
- Separazione tra Mittente e Ricevitore: Il mittente della richiesta non conosce l’handler finale, e la gestione può essere delegata lungo la catena, favorendo l’adesione al principio di responsabilità singola.
- Modificabilità Incrementale: È facile aggiungere o rimuovere nuovi handler senza modificare il codice esistente, rendendo il sistema facilmente estendibile e manutenibile.
- Riuso dei Componenti: Gli handler possono essere riutilizzati in altre catene o contesti, favorendo il riuso del codice e riducendo la duplicazione.
Caso d’Uso
Supponiamo di voler implementare una catena di gestione per il processo di richiesta di supporto tecnico, dove diverse categorie di supporto (ad esempio, livello base, livello avanzato e amministratore) gestiscono le richieste in base alla loro complessità.
Codice PHP
// Interfaccia Handler
interface SupportHandler {
public function setNext(SupportHandler $handler): SupportHandler;
public function handle(string $issue): void;
}
// Handler Concreto per il Supporto Base
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);
}
}
}
// Handler Concreto per il Supporto Avanzato
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);
}
}
}
// Handler Concreto per il Supporto Amministrativo
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);
}
}
}
// Utilizzo
$basicHandler = new BasicSupportHandler();
$advancedHandler = new AdvancedSupportHandler();
$adminHandler = new AdminSupportHandler();
// Costruzione della catena di responsabilità
$basicHandler->setNext($advancedHandler)->setNext($adminHandler);
// Emettiamo una richiesta
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");
Spiegazione
- BasicSupportHandler, AdvancedSupportHandler, e AdminSupportHandler sono i diversi handler che gestiscono vari livelli di problemi di supporto.
- Ogni handler decide se può gestire la richiesta o la passa al successivo nella catena tramite il metodo setNext().
- Se una richiesta non viene gestita da nessun handler, viene semplicemente ignorata o si può aggiungere un fallback.
Conclusione
Il Chain of Responsibility Pattern è ideale per scenari in cui le richieste devono essere elaborate in modo flessibile da una serie di handler potenzialmente indipendenti. Rende il sistema più estensibile e più facile da mantenere, riducendo la complessità del codice e migliorando la leggibilità.