Design patterns: Command

Mauto 3 min

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

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.

TORNA ALLA GUIDA

content_copy Copiato