Allow an object to alter its behaviour when its internal state changes — the object will appear to change its class.
State allows an object (Context) to change its behaviour when its internal state changes. Instead of giant if-else or switch blocks checking state, each state is encapsulated in its own class. The Context delegates all state-dependent behaviour to the current State object.
From the client's perspective the Context behaves differently at different times — as if it changed its class — but the Context class itself never changes.
You're modelling a traffic light. It has states: Red, Yellow, Green. In each state, next() transitions to a different state and getSignal() returns a different string. With if-else: one massive method checking the current state for every operation. Add a new state and every method needs updating.
State pattern puts Red, Yellow, Green each in their own class. Adding a new state is adding one new class — zero changes to existing states or the Context.
// State interface public interface VendingState { void insertCoin(VendingMachine ctx); void pressButton(VendingMachine ctx); void dispense(VendingMachine ctx); } // Context public class VendingMachine { private VendingState state; private int stock; public VendingMachine(int stock) { this.stock = stock; state = new IdleState(); // initial state } public void setState(VendingState s) { state = s; } public int getStock() { return stock; } public void decrementStock() { stock--; } // Delegates all behaviour to current state public void insertCoin() { state.insertCoin(this); } public void pressButton() { state.pressButton(this); } public void dispense() { state.dispense(this); } } // Concrete States public class IdleState implements VendingState { public void insertCoin(VendingMachine ctx) { System.out.println("Coin inserted"); ctx.setState(new HasMoneyState()); // transition } public void pressButton(VendingMachine ctx) { System.out.println("Insert coin first"); } public void dispense(VendingMachine ctx) { System.out.println("No coin inserted"); } } public class HasMoneyState implements VendingState { public void insertCoin(VendingMachine ctx) { System.out.println("Already has coin"); } public void pressButton(VendingMachine ctx) { if (ctx.getStock() > 0) { System.out.println("Dispensing..."); ctx.setState(new DispensingState()); } else { System.out.println("Out of stock — returning coin"); ctx.setState(new IdleState()); } } public void dispense(VendingMachine ctx) { System.out.println("Press button first"); } } public class DispensingState implements VendingState { public void insertCoin(VendingMachine ctx) { System.out.println("Please wait"); } public void pressButton(VendingMachine ctx) { System.out.println("Already dispensing"); } public void dispense(VendingMachine ctx) { System.out.println("Item dispensed!"); ctx.decrementStock(); ctx.setState(ctx.getStock() > 0 ? new IdleState() : new OutOfStockState()); } } // Client public class Main { public static void main(String[] args) { VendingMachine vm = new VendingMachine(2); vm.pressButton(); // Insert coin first vm.insertCoin(); // Coin inserted vm.pressButton(); // Dispensing... vm.dispense(); // Item dispensed! } }
LISTEN, SYN_SENT, ESTABLISHED, CLOSE_WAIT etc.NEW, RUNNABLE, BLOCKED, WAITING, TERMINATEDPENDING → CONFIRMED → SHIPPED → DELIVERED → CANCELLEDPlaying, Paused, Stopped states with different button behaviourif (state instanceof RedState) in Context methods — reintroduces the type-checking if-else that State was designed to eliminateRedState imports and instantiates GreenState — states are tightly coupled, making it impossible to add/change states without cascade editsstate.handle(ctx) — it has zero knowledge of which specific state it holdsIllegalStateException — fail fast and loudly rather than silent no-op// ❌ BREAKING — instanceof in Context defeats the pattern public void pressButton() { if (state instanceof IdleState) { // ❌ back to if-else hell System.out.println("Insert coin first"); } else if (state instanceof HasMoneyState) { System.out.println("Dispensing"); } } // ✅ FIX — delegate to state, no instanceof public void pressButton() { state.pressButton(this); // ✅ polymorphic dispatch — Context knows nothing } // ❌ BREAKING — state creates next state directly (tight coupling) public class IdleState implements VendingState { public void insertCoin(VendingMachine ctx) { ctx.setState(new HasMoneyState()); // ❌ IdleState imports HasMoneyState } } // ✅ FIX — use state enum + factory, states reference only enums public enum StateType { IDLE, HAS_MONEY, DISPENSING, OUT_OF_STOCK } public void insertCoin(VendingMachine ctx) { ctx.transitionTo(StateType.HAS_MONEY); // ✅ context resolves the class } // ❌ BREAKING — invalid state action silently ignored public class IdleState implements VendingState { public void dispense(VendingMachine ctx) { } // ❌ silent no-op } // ✅ FIX — fail loudly with meaningful message public void dispense(VendingMachine ctx) { throw new IllegalStateException( "Cannot dispense in IDLE state — insert coin first"); }
state: State field and delegates. States call context.setState(next) to transition.