Introduction
The State Pattern is a behavioral design pattern that allows an object to alter its behavior when its internal state changes. In other words, it lets the object change its class-like behavior without using if-else
or switch
logic scattered across the code.
State Pattern helps cleanly implement state machines and is particularly useful when an object can exist in multiple states with distinct behaviors.
☑️ Core Intent and Participants
Intent: Allow an object to change its behavior when its internal state changes. The object will appear to change its class.
Participants:
- Context: The class whose behavior varies depending on its state.
- State Interface: Declares methods specific to each state.
- Concrete States: Implement behaviors associated with a particular state of the context.
UML Diagram (Textual)
+------------------+
| Context |
+------------------+
| - state: State |
| +setState() |
| +request() | ---> Delegates to state.handle()
+------------------+
|
v
+------------------+ +------------------+
| ConcreteStateA | | ConcreteStateB |
+------------------+ +------------------+
| +handle() | | +handle() |
+------------------+ +------------------+
🚀 Real-World Use Cases
- Document workflow: Draft → Review → Approved → Published
- Vending machine: Idle → HasCoin → Dispensing → OutOfStock
- Traffic light control: Red → Green → Yellow → Red
- TCP connection: Established, Listening, Closed, etc.
- Media player: Playing, Paused, Stopped
💡 Implementation in Java
Step 1: Define the State Interface
public interface State {
void handle();
}
Step 2: Concrete State Implementations
public class PlayingState implements State {
public void handle() {
System.out.println("Media is playing...");
}
}
public class PausedState implements State {
public void handle() {
System.out.println("Media is paused.");
}
}
public class StoppedState implements State {
public void handle() {
System.out.println("Media is stopped.");
}
}
Step 3: Context Class
public class MediaPlayer {
private State state;
public void setState(State state) {
this.state = state;
}
public void pressButton() {
state.handle();
}
}
Step 4: Demo
public class Demo {
public static void main(String[] args) {
MediaPlayer player = new MediaPlayer();
player.setState(new PlayingState());
player.pressButton(); // Media is playing...
player.setState(new PausedState());
player.pressButton(); // Media is paused.
player.setState(new StoppedState());
player.pressButton(); // Media is stopped.
}
}
✅ Pros and Cons
✅ Pros
- Eliminates conditional logic (
if-else
,switch
) - Adds new states easily
- Improves maintainability and readability
- Adheres to Open-Closed Principle
❌ Cons
- Increases number of classes
- Overhead if number of states is small or fixed
- Harder to debug due to indirection
🔥 Anti-patterns and Misuse
- Using enums or
switch
when states have complex logic - Not encapsulating state behavior inside individual classes
- Tightly coupling context with specific state implementations
🔁 Related Patterns Comparison
Pattern | Purpose |
---|---|
State | Model runtime object behavior per state |
Strategy | Vary algorithms interchangeably |
Command | Encapsulate a request |
Template | Define skeleton with customizable steps |
🟡 State vs Strategy: State is for changing behavior based on internal status; Strategy is for changing behavior via external configuration.
🔄 Refactoring Legacy Code
Refactor if-else
ladders or switch
statements based on status enums (e.g., if (status == PLAYING)
) into dedicated state classes.
🧠 Best Practices
- Keep state transitions inside the Context class
- Encapsulate behavior in ConcreteState classes
- Use sealed classes (Java 17+) or enums with lambdas if class overhead is high
- Avoid exposing
State
classes to client code
🧩 Real-World Analogy
Traffic Light Controller
Each light (Red, Green, Yellow) has different behavior. The traffic controller (context) delegates behavior to current state. Changing state changes behavior, not the controller’s logic.
🧪 Java Version Relevance
✅ With Java 17+
Use sealed interfaces for stricter state modeling:
public sealed interface State permits PlayingState, PausedState, StoppedState { ... }
✅ With Java 8+
Use functional interfaces and lambdas for smaller state logic:
player.setState(() -> System.out.println("Media is fast-forwarding..."));
📝 Conclusion and Key Takeaways
- State Pattern is perfect for modeling finite state machines or object behaviors that vary by internal state.
- It replaces ugly conditional logic with clean, modular class structure.
- Highly useful in workflow engines, GUI tools, game development, and more.
✅ Use when: Object behavior changes based on internal state.
🚫 Avoid when: States are few and behavior doesn’t vary much.
❓ FAQ – State Pattern in Java
1. What is the core use of State Pattern?
To allow objects to behave differently depending on their internal state.
2. Is it the same as Strategy Pattern?
No. Strategy is interchangeable behavior; State reflects an object's internal status.
3. Can I use enums instead of classes?
Yes, for small use cases. But not if each state has complex logic.
4. How does State Pattern improve maintainability?
By isolating behavior into state classes and avoiding conditionals.
5. Does State Pattern support adding new states easily?
Yes. Just create a new class implementing the State
interface.
6. What are real-world Java APIs using State Pattern?
State machines in parsers, protocols (TCP), media frameworks, etc.
7. Is State Pattern thread-safe?
Not inherently. Synchronization may be needed in context/state transitions.
8. Can I implement State Pattern using lambdas?
Yes. Java 8+ supports functional style for simple behaviors.
9. Is the pattern suitable for GUI design?
Absolutely. GUI components like buttons, dialogs change state dynamically.
10. Should state objects be reused or created fresh?
Prefer reuse if states are stateless and thread-safe.