Allow an object to alter its behavior when its internal state changes — making the object appear to change its class.
🧠 What Is the State Pattern?
The State Pattern is a behavioral design pattern that:
- Encapsulates state-specific behavior into separate classes.
- Delegates state transitions and behavior to these state objects.
- Avoids conditionals (
if-else,switch-case) spread across methods.
✅ Real-World Use Cases
| System/Domain | Example of States |
|---|---|
| 🎫 Movie Booking | SeatAvailable → Reserved → Paid → Cancelled |
| 🏧 ATM Machine | CardInserted, PinVerified, CashDispensed |
| 🚦 Traffic Signal | Red → Yellow → Green |
| 📶 Network Connection | Connected, Disconnected, Reconnecting |
| 📄 Document Workflow | Draft, Reviewed, Approved, Published |
📌 When to Use It?
✅ Use State Pattern when:
- An object must behave differently depending on internal state.
- You have lots of conditional logic (
if/switch) that varies by state. - State transitions are part of business logic.
🧱 Pattern Structure (LLD)
| Component | Responsibility |
|---|---|
| State (interface) | Declares operations per state |
| ConcreteState | Implements behavior for a specific state |
| Context | Maintains current state and delegates behavior |
📐 UML Diagram
+---------------------+
| Context |
+---------------------+
| - state: State |
| +setState(State) |
| +handle() |
+---------------------+
▲
|
+---------------------+ +------------------------+
| State |<------+ ConcreteStateX |
+---------------------+ +------------------------+
| +handle(Context) | | +handle(Context) |
+---------------------+ +------------------------+
💻 Java Implementation – Vending Machine Example
We’ll model a vending machine with 3 states:
IdleState: Waiting for coin
HasCoinState: Coin inserted, ready to select item
DispensingState: Dispensing item
✅ VendingState.java
public interface VendingState {
void insertCoin(VendingMachine context);
void selectItem(VendingMachine context);
void dispense(VendingMachine context);
}
✅ IdleState.java
public class IdleState implements VendingState {
@Override
public void insertCoin(VendingMachine context) {
System.out.println("🪙 Coin inserted. Ready to select item.");
context.setState(new HasCoinState());
}
@Override
public void selectItem(VendingMachine context) {
System.out.println("❌ Insert coin first.");
}
@Override
public void dispense(VendingMachine context) {
System.out.println("❌ Cannot dispense. No item selected.");
}
}
✅ HasCoinState.java
public class HasCoinState implements VendingState {
@Override
public void insertCoin(VendingMachine context) {
System.out.println("❌ Coin already inserted.");
}
@Override
public void selectItem(VendingMachine context) {
System.out.println("📦 Item selected. Dispensing now...");
context.setState(new DispensingState());
}
@Override
public void dispense(VendingMachine context) {
System.out.println("❌ Select an item first.");
}
}
✅ DispensingState.java
public class DispensingState implements VendingState {
@Override
public void insertCoin(VendingMachine context) {
System.out.println("❌ Please wait. Dispensing in progress.");
}
@Override
public void selectItem(VendingMachine context) {
System.out.println("❌ Already dispensing.");
}
@Override
public void dispense(VendingMachine context) {
System.out.println("✅ Item dispensed. Back to idle.");
context.setState(new IdleState());
}
}
✅ VendingMachine.java (Context)
public class VendingMachine {
private VendingState currentState;
public VendingMachine() {
this.currentState = new IdleState(); // Initial state
}
public void setState(VendingState state) {
this.currentState = state;
}
public void insertCoin() {
currentState.insertCoin(this);
}
public void selectItem() {
currentState.selectItem(this);
}
public void dispense() {
currentState.dispense(this);
}
}
✅ Client.java
public class StatePatternDemo {
public static void main(String[] args) {
VendingMachine machine = new VendingMachine();
machine.selectItem(); // ❌ Insert coin first.
machine.insertCoin(); // 🪙 Coin inserted.
machine.insertCoin(); // ❌ Already inserted
machine.selectItem(); // 📦 Dispensing
machine.dispense(); // ✅ Dispensed, back to Idle
}
}
🧪 Output
❌ Insert coin first.
🪙 Coin inserted. Ready to select item.
❌ Coin already inserted.
📦 Item selected. Dispensing now...
✅ Item dispensed. Back to idle.
✨ Benefits
| Benefit | Description |
|---|---|
| ✅ Open/Closed | Easily add new states without touching context |
| ✅ Clean code | No complex if-else for state logic |
| ✅ Maintainability | Each state is isolated, testable, reusable |
| ✅ Realistic modeling | Closer to real-world workflows |
⚠️ Limitations
- ❌ Number of classes can grow with complex state machines
- ⚠️ Context object may need to expose internals (setState)
- 🔄 May be overkill for simple linear flows
🧾 Summary
| Aspect | Value |
|---|---|
| Pattern Type | Behavioral |
| Intent | Change behavior by state |
| Java Features Used | Interfaces, Polymorphism, Delegation |
| Ideal for | Workflow engines, UI flows, devices, FSMs |
🎯 Interview Tip
If asked to build a Ticket Booking System, Document Approval Workflow, or Game Character State, the State Pattern is often the cleanest and most extensible design choice.