Design patterns: State
Lo State Pattern è un pattern comportamentale che permette a un oggetto di modificare il proprio comportamento in base al proprio stato interno. Questo pattern è utile quando un oggetto deve cambiare il suo comportamento durante il runtime a seconda di uno stato che varia dinamicamente. Invece di usare una serie di condizionali per gestire i vari stati, il pattern incapsula ogni stato in una classe separata, permettendo un design più ordinato e scalabile.
Punti di Forza
- Riduzione del codice condizionale complesso: Evita l’uso di numerosi
if-else
oswitch-case
per determinare il comportamento in base allo stato. - Aggiunta di nuovi stati: Aggiungere nuovi stati è semplice e non richiede modifiche alle classi esistenti, favorendo l’estensibilità del codice.
- Separazione delle responsabilità: Ogni stato viene incapsulato nella propria classe, separando chiaramente il comportamento specifico di ogni stato.
- Facilita il mantenimento: Modifiche al comportamento di un singolo stato non impattano le altre parti del codice.
- Migliore gestione del ciclo di vita degli stati: Il pattern permette di gestire le transizioni tra stati in modo esplicito e controllato.
Caso d’uso: Macchina da caffè
Immagina di avere una macchina da caffè che può trovarsi in diversi stati: Senza moneta, Con moneta, Erogazione caffè. A seconda dello stato, l’interazione con la macchina cambia.
Codice PHP
// Interfaccia per gli stati
interface CoffeeMachineState {
public function insertCoin();
public function pressButton();
public function dispense();
}
// Stato Senza Moneta
class NoCoinState implements CoffeeMachineState {
private $machine;
public function __construct($machine) {
$this->machine = $machine;
}
public function insertCoin() {
echo "Moneta inserita.\n";
$this->machine->setState($this->machine->getHasCoinState());
}
public function pressButton() {
echo "Inserisci una moneta prima di premere il pulsante.\n";
}
public function dispense() {
echo "Nessun caffè erogato.\n";
}
}
// Stato Con Moneta
class HasCoinState implements CoffeeMachineState {
private $machine;
public function __construct($machine) {
$this->machine = $machine;
}
public function insertCoin() {
echo "Moneta già inserita.\n";
}
public function pressButton() {
echo "Pulsante premuto. Erogazione in corso.\n";
$this->machine->setState($this->machine->getDispensingState());
}
public function dispense() {
echo "Premi il pulsante prima di ottenere il caffè.\n";
}
}
// Stato di Erogazione
class DispensingState implements CoffeeMachineState {
private $machine;
public function __construct($machine) {
$this->machine = $machine;
}
public function insertCoin() {
echo "Attendi la fine dell'erogazione.\n";
}
public function pressButton() {
echo "Attendi la fine dell'erogazione.\n";
}
public function dispense() {
echo "Caffè erogato.\n";
$this->machine->setState($this->machine->getNoCoinState());
}
}
// Classe Macchina da Caffè
class CoffeeMachine {
private $noCoinState;
private $hasCoinState;
private $dispensingState;
private $currentState;
public function __construct() {
$this->noCoinState = new NoCoinState($this);
$this->hasCoinState = new HasCoinState($this);
$this->dispensingState = new DispensingState($this);
$this->currentState = $this->noCoinState;
}
public function setState($state) {
$this->currentState = $state;
}
public function insertCoin() {
$this->currentState->insertCoin();
}
public function pressButton() {
$this->currentState->pressButton();
}
public function dispense() {
$this->currentState->dispense();
}
public function getNoCoinState() {
return $this->noCoinState;
}
public function getHasCoinState() {
return $this->hasCoinState;
}
public function getDispensingState() {
return $this->dispensingState;
}
}
// Esempio d'uso
$coffeeMachine = new CoffeeMachine();
// Interazioni con la macchina da caffè
$coffeeMachine->pressButton(); // Inserisci una moneta prima di premere il pulsante.
$coffeeMachine->insertCoin(); // Moneta inserita.
$coffeeMachine->pressButton(); // Pulsante premuto. Erogazione in corso.
$coffeeMachine->dispense(); // Caffè erogato.
$coffeeMachine->pressButton(); // Inserisci una moneta prima di premere il pulsante.
Spiegazione
- Interfaccia CoffeeMachineState: Definisce i metodi che ogni stato della macchina dovrà implementare.
- Stati concreti (NoCoinState, HasCoinState, DispensingState): Implementano l’interfaccia e gestiscono il comportamento della macchina a seconda dello stato.
- Classe CoffeeMachine: Contiene una serie di stati e gestisce le transizioni tra di essi.
- Uso della Macchina: Le transizioni tra gli stati avvengono in base alle interazioni dell’utente con la macchina, senza bisogno di gestire esplicitamente logiche condizionali complesse.