# Design patterns: State, cos'è e come usarlo (esempio PHP)

> Lo State pattern spiegato: a cosa serve, i vantaggi e un esempio in PHP con una macchina del caffè che cambia comportamento a seconda dello stato (senza moneta, con moneta, in erogazione).

- URL: https://mautoblog.com/posts/design-patterns/state/
- Lingua: it
- Pubblicato: 2024-09-27
- Aggiornato: 2026-10-16
- Autore: Mauto
- Categorie: Sviluppo Software, Tecnologia
- Tags: Architettura Software, Best Practices, PHP
- Guida: Design Patterns (parte 21)
- Traduzione (en): https://mautoblog.com/en/posts/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
1. **Riduzione del codice condizionale complesso**: Evita l'uso di numerosi `if-else` o `switch-case` per determinare il comportamento in base allo stato.
2. **Aggiunta di nuovi stati**: Aggiungere nuovi stati richiede modifiche localizzate (una nuova classe e poche transizioni), senza toccare condizionali sparsi nel contesto, favorendo l'estensibilità del codice.
3. **Separazione delle responsabilità**: Ogni stato viene incapsulato nella propria classe, separando chiaramente il comportamento specifico di ogni stato.
4. **Facilita il mantenimento**: Modifiche al comportamento di un singolo stato non impattano le altre parti del codice.
5. **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
```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**: Mantiene lo stato corrente e gli delega le richieste dell'utente; le transizioni tra stati sono decise dagli stati concreti.
- 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.

[TORNA ALLA GUIDA](/posts/design-patterns/)
