State Pattern — Evitare troppi condizionali
Come rappresentare in modo esplicito gli stati di un oggetto quando il suo comportamento cambia insieme a essi.
Spesso costruiamo una classe il cui comportamento dipende da una combinazione di valori interni. La prima implementazione si affida normalmente a un numero crescente di if o a un grande switch. Man mano che aggiungiamo modalità e azioni, la logica diventa sempre più difficile da comprendere e mantenere.
Il pattern State della Gang of Four affronta questo problema permettendo a un oggetto di cambiare comportamento quando cambia il suo stato interno.
Un orologio con due pulsanti
Immaginiamo un orologio digitale con due pulsanti: MODE e CHANGE. Premendo MODE, l’orologio passa attraverso tre stati:
- visualizzazione normale;
- modifica dell’ora;
- modifica dei minuti.
Il significato di CHANGE dipende dallo stato corrente:
- nella visualizzazione normale accende la luce;
- durante la modifica dell’ora incrementa l’ora;
- durante la modifica dei minuti incrementa i minuti.
Un’implementazione condizionale potrebbe iniziare così:
void pressChange() {
if (state == NORMAL_DISPLAY) {
activateLight();
} else if (state == UPDATING_HOURS) {
hours++;
} else if (state == UPDATING_MINUTES) {
minutes++;
}
}
Il codice funziona, ma ogni nuovo stato ci obbliga a rivedere più operazioni. Le regole appartenenti a uno stato sono sparse tra diversi metodi, rendendo difficile vedere il comportamento completo dell’orologio.
Struttura del pattern

Il pattern separa tre ruoli:
- Context è l’oggetto utilizzato dal client. Conserva un riferimento allo stato corrente e delega a esso le azioni dipendenti dallo stato.
- State è un’interfaccia o una classe astratta che definisce le azioni condivise da ogni possibile stato.
- Concrete State rappresenta una modalità del context. Implementa quelle azioni e può spostare il context verso un altro stato.
Nel nostro orologio, il context delega entrambi i pulsanti:
public class Clock {
private ClockState state;
int hour;
int minute;
public Clock() {
state = new NormalDisplayState(this);
}
void setState(ClockState state) {
this.state = state;
}
public void pressMode() {
state.pressMode();
}
public void pressChange() {
state.pressChange();
}
}
Il contratto comune è piccolo:
public abstract class ClockState {
protected final Clock clock;
protected ClockState(Clock clock) {
this.clock = clock;
}
public abstract void pressMode();
public abstract void pressChange();
}
Ogni stato concreto mantiene raccolte le proprie regole:
public class NormalDisplayState extends ClockState {
public NormalDisplayState(Clock clock) {
super(clock);
}
public void pressMode() {
clock.setState(new UpdatingHourState(clock));
}
public void pressChange() {
System.out.println("Light on");
}
}
public class UpdatingHourState extends ClockState {
public UpdatingHourState(Clock clock) {
super(clock);
}
public void pressMode() {
clock.setState(new UpdatingMinuteState(clock));
}
public void pressChange() {
clock.hour = (clock.hour + 1) % 24;
}
}
public class UpdatingMinuteState extends ClockState {
public UpdatingMinuteState(Clock clock) {
super(clock);
}
public void pressMode() {
clock.setState(new NormalDisplayState(clock));
}
public void pressChange() {
clock.minute = (clock.minute + 1) % 60;
}
}
I vantaggi
Ogni stato descrive ora un insieme coerente di comportamenti e transizioni. Aggiungere uno stato richiede ancora una progettazione consapevole, ma non richiede più un nuovo ramo in ogni metodo del context che dipende dallo stato.
Il pattern è particolarmente utile quando gli stati rappresentano concetti importanti e ciascuno porta con sé comportamenti diversi. Per una piccola condizione fissa, un semplice if o switch può restare più chiaro. L’obiettivo non è eliminare ogni condizionale, ma rendere visibile la macchina a stati quando è già parte del problema.
Riferimenti
- Game Programming Patterns: State
- Design Patterns: Elements of Reusable Object-Oriented Software, Gamma et al.