Behavioral · Gang of Four
State pattern
The State pattern makes each mode of an object a separate class. The same method names stay on the context; each state class decides what those methods mean and which state comes next.
Example: a vending machine has insert coin, select, and refund. In idle, a coin moves you to has-coin. In has-coin, select vends the item. In sold-out, a coin should come back. Same buttons, different behavior — that is when State objects are a better fit than a status enum alone.
Life of the machine
The current state picks the next state
The holds a State. Each state implements the actions. live in the states. Invalid actions are no-ops or exceptions inside the state that received them.
Read the diagram definition
stateDiagram-v2
[*] --> Idle
Idle --> HasCoin: coin
HasCoin --> Idle: select + stock left
HasCoin --> SoldOut: select + empty
SoldOut --> SoldOut: coin refunds
HasCoin --> HasCoin: extra coinList the states
Idle, HasCoin, Sold, SoldOut — or NoCard, HasCard, Authenticated.
List the actions
Every button is a method on the State interface.
Put transitions in the states
Idle.insertCoin credits, then sets HasCoin. HasCoin.select may set SoldOut.
Keep the context thin
VendingMachine holds money and inventory, and delegates buttons.
State vs Strategy
Who decides the swap, and why?
Both swap an object the context holds. Strategy is an algorithm you pick from the outside — nearest spot, airport pricing — and it stays until you replace it. The strategy does not usually appoint the next strategy. State is the object’s life: Idle becomes HasCoin because a coin arrived. If you are choosing how to price, that is Strategy. If you are modeling a vending machine or ATM session, that is State.
State · what am I right now?
- Idle --coin--> HasCoin --select--> Sold
- The current state picks the next state
- Buttons mean different things in different states
- ATM session, elevator, vending
Strategy · how do I do this job?
- ParkingLot holds AllocationStrategy
- You pick nearest vs random; it does not “become” random
- Injected in the constructor or by a factory
- Pricing, allocation, win rules
State objects that never transition, and that you pick in a constructor, are strategies wearing the wrong name. A parking lot can use both: a State for a gate session is unusual; a Strategy for fees is expected.
Java: vending buttons mean different things
select on Idle is an handled in Idle. Idle.insertCoin credits, then moves to HasCoin. HasCoin decides whether the next state is Idle or SoldOut. The machine does not switch on an enum inside insertCoin().
interface State {
void insertCoin(VendingMachine m);
void select(VendingMachine m, String sku);
}
class Idle implements State {
public void insertCoin(VendingMachine m) {
m.addCredit(); // credit before / with the transition
m.setState(new HasCoin());
}
public void select(VendingMachine m, String sku) { /* ignore */ }
}
class HasCoin implements State {
public void insertCoin(VendingMachine m) { m.addCredit(); }
public void select(VendingMachine m, String sku) {
if (!m.dispense(sku)) return;
m.setState(m.isEmpty() ? new SoldOut() : new Idle());
}
}
class SoldOut implements State {
public void insertCoin(VendingMachine m) { m.refund(); }
public void select(VendingMachine m, String sku) { /* ignore */ }
}
class VendingMachine {
private State state = new Idle();
void insertCoin() { state.insertCoin(this); }
void select(String sku) { state.select(this, sku); }
void setState(State s) { this.state = s; }
}When a status enum is enough
Do not hide a validator chain as State
A switch that grew into the pattern’s costume
Do not rename the enum and call it done. If each case is a page, extract objects.
Using State where you meant Strategy
Pricing that changes by vehicle type is not a life cycle. It is a policy you inject.
ATM session as a second picture
NoCard, HasCard, Authenticated, Dispensing. insertCard, enterPin, withdraw mean different things. The authenticated state may start a withdrawal chain; the chain is not the state machine. Draw both if they ask for ATM.
NoCard
insertCard → HasCard. PIN is ignored.
HasCard
enterPin → Authenticated or stay. eject → NoCard.
Authenticated
withdraw starts checks. eject → NoCard.
Dispensing
buttons ignored until cash finishes.
Java: refund is a method on every state
Idle refunds nothing. HasCoin returns credit and goes Idle. SoldOut already refunded the coin. No switch in VendingMachine.refund().
interface State {
void insertCoin(VendingMachine m);
void select(VendingMachine m, String sku);
void refund(VendingMachine m);
}
class HasCoin implements State {
public void refund(VendingMachine m) {
m.returnCredit();
m.setState(new Idle());
}
}Elevator is State plus a queue
MovingUp / MovingDown / Idle decide what a floor button means. The queue of requests is data the context holds. Do not put the whole scheduler inside each state class.
Enums are not a failure
Ticket status as an enum is the right size. State objects are for behavior-heavy machines. State that clearly so they do not think you pattern-match everything.
How to explain this in an interview
“Buttons mean different things in different states. The current state appoints the next.” If they change pricing, that is Strategy — do not put fees inside HasCoin.