State Pattern
IntermediateAllows an object to alter its behavior when its internal state changes, appearing to change its class.
Overview
The State pattern models a Finite State Machine (FSM) in OOP. Instead of a massive switch/if-else on a status enum, each state is its own class implementing a State interface. The Context delegates behavior to its current State object. When a transition occurs, the Context replaces the current state with the new one. This eliminates the "illegal operation in state X" defensive checks scattered throughout code — invalid operations simply do nothing or throw in the wrong state class. Classic examples: traffic light, vending machine, order lifecycle (Pending → Confirmed → Shipped → Delivered), TCP connection states.
Vending Machine State Machine
A vending machine has four states: Idle, HasMoney, Dispensing, OutOfStock. Each state handles insertCoin(), selectProduct(), dispense(), and refund() differently. Invalid operations in a state throw or silently ignore.
// State interface
public interface VendingMachineState {
void insertCoin(VendingMachine machine, double amount);
void selectProduct(VendingMachine machine, String product);
void dispense(VendingMachine machine);
void refund(VendingMachine machine);
}
// Context
public class VendingMachine {
private VendingMachineState state;
private double balance = 0;
private int stockCount = 10;
public VendingMachine() {
this.state = new IdleState();
}
public void setState(VendingMachineState state) { this.state = state; }
public double getBalance() { return balance; }
public void setBalance(double b) { this.balance = b; }
public int getStock() { return stockCount; }
public void decrementStock() { stockCount--; }
// Delegates all behavior to current state
public void insertCoin(double amount) { state.insertCoin(this, amount); }
public void selectProduct(String product) { state.selectProduct(this, product); }
public void dispense() { state.dispense(this); }
public void refund() { state.refund(this); }
}
// Concrete States
public class IdleState implements VendingMachineState {
@Override
public void insertCoin(VendingMachine m, double amount) {
m.setBalance(m.getBalance() + amount);
System.out.println("Coin inserted: ₹" + amount + ". Balance: ₹" + m.getBalance());
m.setState(new HasMoneyState()); // transition
}
@Override public void selectProduct(VendingMachine m, String p) { System.out.println("Please insert coin first"); }
@Override public void dispense(VendingMachine m) { System.out.println("Please insert coin first"); }
@Override public void refund(VendingMachine m) { System.out.println("No money to refund"); }
}
public class HasMoneyState implements VendingMachineState {
private String selectedProduct;
@Override
public void insertCoin(VendingMachine m, double amount) {
m.setBalance(m.getBalance() + amount);
System.out.println("Added ₹" + amount + ". Total: ₹" + m.getBalance());
}
@Override
public void selectProduct(VendingMachine m, String product) {
double price = 20.0; // simplified
if (m.getBalance() >= price) {
this.selectedProduct = product;
System.out.println("Selected: " + product);
m.setState(new DispensingState(product, price));
} else {
System.out.println("Insufficient balance. Need ₹" + price);
}
}
@Override public void dispense(VendingMachine m) { System.out.println("Please select a product first"); }
@Override
public void refund(VendingMachine m) {
System.out.println("Refunding ₹" + m.getBalance());
m.setBalance(0);
m.setState(new IdleState());
}
}
public class DispensingState implements VendingMachineState {
private final String product;
private final double price;
public DispensingState(String product, double price) {
this.product = product; this.price = price;
}
@Override
public void dispense(VendingMachine m) {
System.out.println("Dispensing: " + product);
m.setBalance(m.getBalance() - price);
m.decrementStock();
if (m.getBalance() > 0) System.out.println("Change: ₹" + m.getBalance());
m.setBalance(0);
m.setState(m.getStock() > 0 ? new IdleState() : new OutOfStockState());
}
@Override public void insertCoin(VendingMachine m, double a) { System.out.println("Dispensing in progress"); }
@Override public void selectProduct(VendingMachine m, String p) { System.out.println("Dispensing in progress"); }
@Override public void refund(VendingMachine m) { System.out.println("Cannot refund while dispensing"); }
}
public class OutOfStockState implements VendingMachineState {
@Override public void insertCoin(VendingMachine m, double a) { System.out.println("Out of stock — coin returned"); }
@Override public void selectProduct(VendingMachine m, String p) { System.out.println("Out of stock"); }
@Override public void dispense(VendingMachine m) { System.out.println("Out of stock"); }
@Override public void refund(VendingMachine m) { System.out.println("No money inserted"); }
}
// Usage
VendingMachine vm = new VendingMachine();
vm.insertCoin(20.0);
vm.selectProduct("Water");
vm.dispense();Key Points to Remember
- 1State eliminates if-else/switch chains on status fields — each state is a class.
- 2Context delegates all operations to the current State; State transitions by replacing the state object.
- 3State classes can reference the Context to trigger transitions (state.dispense() calls machine.setState(...)).
- 4Invalid operations in a state are handled locally (throw or silently ignore) — no scattered null checks.
- 5Order lifecycle (PENDING→CONFIRMED→SHIPPED→DELIVERED) and TCP connection are real-world State machines.
Interview Questions
Sign in to ask AriaWhat is the difference between State and Strategy patterns?
How does State pattern eliminate if-else chains?
Design a traffic light system using the State pattern.
Who should be responsible for state transitions — Context or State?
How would you persist and restore a State machine across application restarts?
Ask Aria about State Pattern
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.