Design patterns: Command
Il Command Pattern è un pattern comportamentale che trasforma una richiesta in un oggetto autonomo, consentendo di parametrizzare i metodi con richieste diverse, accodare o registrare richieste e supportare operazioni annullabili (undo). Il pattern separa l’oggetto che invia la richiesta da quello che la esegue, migliorando la flessibilità e la manutenibilità del codice.
Punti di Forza
- Separazione dei Responsabili: Decoupla l’invoker (chi invia il comando) dal receiver (chi esegue il comando), migliorando la modularità e permettendo di cambiare le classi indipendentemente l’una dall’altra.
- Supporto per Operazioni Annullabili (Undo/Redo): Il pattern permette di memorizzare lo stato prima dell’esecuzione di un comando, facilitando l’implementazione di operazioni annullabili o ripetibili.
- Facilità di Estensione: Aggiungere nuovi comandi è semplice, poiché ogni comando è incapsulato in una classe separata che implementa una comune interfaccia. Questo rende il sistema altamente estendibile.
- Flessibilità di Accodamento e Logging: Permette di accodare, ritardare o loggare i comandi, creando sistemi in cui l’esecuzione delle richieste può essere posticipata o monitorata.
- Parametrizzazione delle Richieste: Le richieste possono essere trattate come oggetti e passate come parametri, permettendo un livello di astrazione e configurazione dinamica delle operazioni da eseguire.
Caso d’Uso
Supponiamo di voler gestire un semplice sistema di controllo remoto per dispositivi elettronici, come TV e luci. Ogni dispositivo può ricevere comandi come “Accendi” o “Spegni”. Utilizziamo il Command Pattern per modellare questi comandi.
Codice PHP
// Interfaccia del comando
interface Command {
public function execute();
}
// Receiver - Classe del dispositivo TV
class TV {
public function turnOn() {
echo "TV accesa\n";
}
public function turnOff() {
echo "TV spenta\n";
}
}
// Receiver - Classe della luce
class Light {
public function turnOn() {
echo "Luce accesa\n";
}
public function turnOff() {
echo "Luce spenta\n";
}
}
// Comando per accendere la TV
class TurnOnTVCommand implements Command {
private $tv;
public function __construct(TV $tv) {
$this->tv = $tv;
}
public function execute() {
$this->tv->turnOn();
}
}
// Comando per spegnere la TV
class TurnOffTVCommand implements Command {
private $tv;
public function __construct(TV $tv) {
$this->tv = $tv;
}
public function execute() {
$this->tv->turnOff();
}
}
// Comando per accendere la luce
class TurnOnLightCommand implements Command {
private $light;
public function __construct(Light $light) {
$this->light = $light;
}
public function execute() {
$this->light->turnOn();
}
}
// Comando per spegnere la luce
class TurnOffLightCommand implements Command {
private $light;
public function __construct(Light $light) {
$this->light = $light;
}
public function execute() {
$this->light->turnOff();
}
}
// Invoker - Il telecomando
class RemoteControl {
private $commands = [];
public function setCommand($slot, Command $command) {
$this->commands[$slot] = $command;
}
public function pressButton($slot) {
if (isset($this->commands[$slot])) {
$this->commands[$slot]->execute();
} else {
echo "Nessun comando assegnato al pulsante $slot\n";
}
}
}
// Utilizzo
$tv = new TV();
$light = new Light();
$remoteControl = new RemoteControl();
$remoteControl->setCommand(1, new TurnOnTVCommand($tv));
$remoteControl->setCommand(2, new TurnOffTVCommand($tv));
$remoteControl->setCommand(3, new TurnOnLightCommand($light));
$remoteControl->setCommand(4, new TurnOffLightCommand($light));
// Esegui i comandi tramite il telecomando
$remoteControl->pressButton(1); // TV accesa
$remoteControl->pressButton(3); // Luce accesa
$remoteControl->pressButton(4); // Luce spenta
$remoteControl->pressButton(2); // TV spenta
Spiegazione
- Command è l’interfaccia che tutti i comandi devono implementare.
- TurnOnTVCommand, TurnOffTVCommand, TurnOnLightCommand, e TurnOffLightCommand sono le implementazioni concrete del comando, ciascuna delle quali rappresenta un’azione specifica.
- RemoteControl è l’invoker che richiama i comandi senza sapere come esattamente essi funzionano.
- Questo esempio permette di espandere facilmente il sistema, aggiungendo nuovi dispositivi o nuovi comandi semplicemente creando nuove classi di comando.
Conclusione
Il Command Pattern è un ottimo strumento per disaccoppiare il client che invoca un’azione dal codice che la esegue. Questo migliora la manutenibilità del codice, consente di implementare con facilità operazioni annullabili (undo/redo) e supporta un utilizzo flessibile delle richieste.