State Pattern enables objects to change behavior flexibly when internal state changes, eliminating complex conditional statements and improving code extensibility.

No table of contents available for this article
State Pattern is a behavioral design pattern from the Gang of Four that allows an object to alter its behavior automatically when its internal state changes. From the outside, it appears as though the object has "changed its class."
Imagine you're using a music player app. The "Play" button behaves differently depending on the state:
Stopped: Press Play → Start playing music
Playing: Press Play → Pause music
Paused: Press Play → Resume playing music
Instead of writing a pile of if-else or switch-case statements to handle each scenario, State Pattern helps you organize code more cleanly and maintainably.
Usage frequency: Medium
In reality, many objects have a finite number of states and can transition between them. Each state has its own unique behaviors and characteristics.
Real-world Example: Document Class
A document can be in 3 states:
Draft: Being written
Moderation: Awaiting approval
Published: Public for everyone
The Publish() method behaves differently in each state:
Draft → Transition to Moderation
Moderation → Transition to Published
Published → Do nothing (already published)
Traditional Approach:
csharp
public void Publish() {
if (state == "Draft") {
state = "Moderation";
} else if (state == "Moderation") {
state = "Published";
} else if (state == "Published") {
// Do nothing
}
}
```
**Issues That Arise:**
- Adding more states (e.g., Archived, Rejected, PendingReview...) makes code messier
- Difficult to maintain when logic changes
- Violates Open/Closed Principle
- Repeated code everywhere
### Another Example: TCP Connection
A network connection TCPConnection can be in states:
- **Established**: Connection is active
- **Listening**: Listening for connections
- **Closed**: Connection closed
When receiving an Open() request, behavior differs:
- If **Closed** → Open new connection
- If **Established** → Return error (already open)
### Solution - State Pattern Approach
State Pattern proposes:
1. **Create separate classes** for each state
2. **Extract specific behavior** into those classes
3. **Context object** only holds a reference to current state and delegates work
**Benefits:**
- Clean, readable code
- Easy to add new states
- Each state manages its own logic
## 3. State Pattern Architecture
### Structure Diagram
```
Context ---------> State (interface)
^
|
+------------+------------+
| | |
ConcreteStateA ConcreteStateB ConcreteStateC1. Context
Main class that clients use
Maintains reference to a ConcreteState object
Doesn't handle state logic directly, delegates to state object
Provides method to change state
2. State Interface
Defines common interface for all ConcreteStates
Declares methods that Context can call
Can be an interface or abstract class
3. ConcreteState
Implements State interface
Contains specific logic for each state
Can trigger transitions to other states
A music player has states:
PlayingState:
Play() → Pause (transition to PausedState)
Stop() → Stop (transition to StoppedState)
PausedState:
Play() → Resume (transition to PlayingState)
Stop() → Stop (transition to StoppedState)
StoppedState:
Play() → Start (transition to PlayingState)
Stop() → Do nothing
Context (player) simply calls currentState.Play() without needing to know the current state.
1. Single Responsibility Principle (SRP)
Each state separated into its own class
Clear logic, easy to find and fix bugs
2. Open/Closed Principle (OCP)
Add new states without modifying existing code
Just create new class implementing State interface
3. Eliminates complex conditionals
No more long if-else or switch-case chains
Context code becomes clean
4. Easy to maintain and extend
Changing one state's logic doesn't affect others
Easy to test each state independently
5. Clear state transitions
Transition flow centrally managed
Easy to debug and trace
1. Increases number of classes
Each state is a separate class
Codebase can become complex with many states
2. Overkill for simple cases
If only 2-3 states that rarely change
Simple if-else might suffice
3. Initial complexity
Requires time to design structure
Beginners might find it difficult
1. Object has many states (5+ states)
Example: Order (Pending → Confirmed → Shipped → Delivered → Completed)
2. Behavior changes complexity based on state
Example: Vending Machine, Traffic Light, Game Character
3. Code has too many if-else/switch-case
csharp
// Code smell
if (status == "draft") { ... }
else if (status == "pending") { ... }
else if (status == "approved") { ... }
// ... 10 more conditions4. Need to extend with more states in the future
Example: Workflow may add new approval steps
5. Duplicate code across states
Only 2-3 simple states
States rarely change
Very simple logic
csharp
abstract class State
{
protected Context _context;
public void SetContext(Context context)
{
this._context = context;
}
public abstract void Handle1();
public abstract void Handle2();
}csharp
class Context
{
private State _state = null;
public Context(State state)
{
this.TransitionTo(state);
}
// Method to transition state
public void TransitionTo(State state)
{
Console.WriteLine($"Context: Transition to {state.GetType().Name}.");
this._state = state;
this._state.SetContext(this);
}
// Delegate handling to current state
public void Request1()
{
this._state.Handle1();
}
public void Request2()
{
this._state.Handle2();
}
}csharp
class ConcreteStateA : State
{
public override void Handle1()
{
Console.WriteLine("ConcreteStateA handles request1.");
Console.WriteLine("ConcreteStateA wants to change the state of the context.");
this._context.TransitionTo(new ConcreteStateB());
}
public override void Handle2()
{
Console.WriteLine("ConcreteStateA handles request2.");
}
}
class ConcreteStateB : State
{
public override void Handle1()
{
Console.WriteLine("ConcreteStateB handles request1.");
}
public override void Handle2()
{
Console.WriteLine("ConcreteStateB handles request2.");
Console.WriteLine("ConcreteStateB wants to change the state of the context.");
this._context.TransitionTo(new ConcreteStateA());
}
}csharp
class Program
{
static void Main(string[] args)
{
// Initialize context with initial state
var context = new Context(new ConcreteStateA());
context.Request1(); // StateA → transition to StateB
context.Request2(); // StateB → transition back to StateA
}
}
```
### Output:
```
Context: Transition to ConcreteStateA.
ConcreteStateA handles request1.
ConcreteStateA wants to change the state of the context.
Context: Transition to ConcreteStateB.
ConcreteStateB handles request2.
ConcreteStateB wants to change the state of the context.
Context: Transition to ConcreteStateA.State Pattern:
States auto-transition
Context changes state
States know about each other
Strategy Pattern:
Strategy set externally
Client changes strategy
Strategies are independent
Bridge: Separates abstraction from implementation
State: Manages behavior changes based on state
State can be considered an extension of Strategy. Both:
Based on composition
Delegate work to other objects
But solve different problems
State doesn't restrict dependencies between concrete states, allowing them to freely transition the context.
State Pattern is an excellent solution when you need to:
Manage objects with many complex states
Avoid "spaghetti code" with piles of if-else
Easily add new states in the future
Note: Don't overuse it! For simple cases, a few if-else statements are still a more reasonable choice.
If you found this article helpful, explore more in the Design Patterns Series to enhance your programming skills!
[1] Refactoring.Guru. https://refactoring.guru/design-patterns
[2] Design Patterns for Dummies, Steve Holzner, PhD
[3] Head First Design Patterns, Eric Freeman
[4] Gang of Four Design Patterns 4.0
[5] Dive into Design Patterns