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.
Top comments (0)