This content originally appeared on DEV Community and was authored by DevCorner2
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 |
---|---|
![]() |
SeatAvailable β Reserved β Paid β Cancelled |
![]() |
CardInserted , PinVerified , CashDispensed
|
![]() |
Red β Yellow β Green |
![]() |
Connected , Disconnected , Reconnecting
|
![]() |
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 |
---|---|
![]() |
Easily add new states without touching context |
![]() |
No complex if-else for state logic |
![]() |
Each state is isolated, testable, reusable |
![]() |
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.
This content originally appeared on DEV Community and was authored by DevCorner2