DEV Community

Cover image for Design Patterns Simplified: Part 14 — State Pattern (a.k.a. “The Mood Manager”)
Prateek Prabhakar
Prateek Prabhakar

Posted on • Edited on • Originally published at Medium

Design Patterns Simplified: Part 14 — State Pattern (a.k.a. “The Mood Manager”)

The State Pattern falls under the Behavioral category. Why? Because it deals with how an object behaves depending on its internal state and more importantly, how it can change that behavior dynamically at runtime.

Think of it like, when your object is in a “mood", its reactions depend on what mood it is currently in.

Let’s understand this with a story.


The Media Player

Imagine you are building a media player app with two main controls: a Play/Pause toggle button and a Stop button.

Sounds simple, right? But here’s the thing:

  • If the player is Stopped, pressing Play starts playback.
  • If it’s Playing, pressing Play pauses the music.
  • If it’s Paused, pressing Play resumes playback.
  • Pressing Stop at any point stops the music and resets the state.

The key insight is this,

The same Play button behaves differently depending on the current state of the player.

Without the State Pattern, you would be writing messy if-else chains to handle all scenarios.

With the State Pattern, each state (Playing, Paused, Stopped) handles user input its own way.

What Is the State Pattern?

The State Pattern allows an object to change its behavior when its internal state changes as if it changed its class.

Instead of managing the behavior using complex conditionals, the object delegates behavior to state specific classes.

What does the code look like?

To implement such behavior in the application, typically we would have the below components,

  1. State Interface: declares the actions (Play, Pause, Stop).
  2. Concrete State Classes: implement behavior specific to each state.
  3. Context Class (MediaPlayer): maintains the current state reference and delegates action calls.
//Step 1: Define the State Interface
Interface PlayerState
    Method TogglePlayPause(player)
    Method Stop(player)

//Step 2: Define Concrete States
Class PlayingState implements PlayerState
    Method TogglePlayPause(player)
        Print "Pausing playback..."
        player.SetState(new PausedState())

    Method Stop(player)
        Print "Stopping playback..."
        player.SetState(new StoppedState())

Class PausedState implements PlayerState
    Method TogglePlayPause(player)
        Print "Resuming playback..."
        player.SetState(new PlayingState())

    Method Stop(player)
        Print "Stopping playback from pause..."
        player.SetState(new StoppedState())

Class StoppedState implements PlayerState
    Method TogglePlayPause(player)
        Print "Starting playback..."
        player.SetState(new PlayingState())

    Method Stop(player)
        Print "Already stopped."

//Step 3: Define the MediaPlayer Context
Class MediaPlayer
    Property currentState

    Constructor()
        currentState = new StoppedState()

    Method TogglePlayPause()
        currentState.TogglePlayPause(this)

    Method Stop()
        currentState.Stop(this)

//caller logic
player = new MediaPlayer()

player.TogglePlayPause()  // Starting playback...
player.TogglePlayPause()  // Pausing playback...
player.TogglePlayPause()  // Resuming playback...
player.Stop()             // Stopping playback...
player.Stop()             // Already stopped.
Enter fullscreen mode Exit fullscreen mode

Each state class (PlayingState, PausedState, StoppedState) encapsulates the behavior for TogglePlayPause() and Stop() actions relevant to that state. The MediaPlayer object delegates these calls to its current state, allowing dynamic behavior changes without complex conditional logic.
This approach keeps the player’s behavior organized which is easy to extend and intuitive to manage.

What Did We Achieve?

  • No messy if-else chains: All state-specific behavior is encapsulated cleanly.
  • Easily extensible: Adding new states (like Buffering) means adding new classes and no modifications to existing code.
  • Cleaner MediaPlayer class: Just delegates actions. It has no complex logic.

When Should You Use State Pattern?

  • When an object’s behavior varies significantly depending on its state.
  • When complex conditional logic is hard to maintain and understand.
  • When you want to encapsulate each state’s logic for easier testing and modification.

Use Cases?

  • Media players (play/pause/stop)
  • Game characters (idle, running, jumping)
  • Vending machines (waiting for coin, dispensing, out of stock)
  • Traffic lights (green, yellow, red)
  • Network connections (connecting, connected, disconnected)
  • Workflow engines (draft, submitted, approved)

To summarize, it would be apt to say,

“State Pattern is like giving your object a brain that knows when and how to switch gears.”

It gives your objects a “mood” enabling them to behave differently based on their internal condition, without tangled conditional logic. This leads to cleaner, more maintainable code and makes extending behavior easier.

Next up in the series: Visitor Pattern. Let's understand that now!


BONUS: State vs Strategy — A Story to Remember

Imagine you are running a café that serves coffee.

Scenario 1: The Customer’s Mood (State Pattern)

One customer walks in. Depending on their mood - happy, tired, or grumpy, they order and react differently.

  • If they are happy, they smile and say, “I’ll have a cappuccino, please.”
  • If they are tired, they say, “Just a strong black coffee, thanks.”
  • If they are grumpy, they might say, “Make it quick, I’m in a hurry.”

The customer’s mood changes over time. After a sip, they might become happier or more relaxed, changing their behavior accordingly.

This is the State Pattern: the object (customer) changes behavior based on its internal state (mood).

Scenario 2: The Barista’s Tools (Strategy Pattern)
Now, the barista needs to prepare coffee. They can use different brewing methods - espresso machine, French press, or drip coffee maker.
The barista chooses the brewing method based on the customer’s preference or cafe’s style.

Each brewing method is an independent strategy i.e. a different algorithm to make coffee.
Here, the choice of brewing method comes from outside, the barista selects which strategy to use for the task.

This is the Strategy Pattern: The context (barista) uses a chosen strategy (brewing method) to accomplish a task.

The Key Difference:
State Pattern - The object itself controls which behavior to perform by switching between states internally.

Strategy Pattern - The client or context decides which algorithm or strategy to use externally and sticks with it.

Think of it like,

In State, the behavior changes because your mood changed.
In Strategy, the behavior changes because you picked a different tool.

Top comments (0)