State

Intent

State is a behavioral design pattern that allows an object to alter its behavior when its internal state changes. The object will appear to change its class.

Problem

The State pattern is closely related to the concept of Finite State Machine.

Its main idea is that a program can be in one of the several states, which follow each other. The number of states and possible transitions between them is predefined and finite. Depending on the current state, the program behaves differently in response to the same events.

The similar approach can be applied to objects. For instance, the Document object can be in one of the three states: Draft, Moderations and Published. In each state, the publish method will work in a different way:

  • In the first case, it will move the document to moderation.
  • In the second case, it will make the document public, but only if the current user is the administrator.
  • In the third case, it won't do anything at all.

State machines are usually implemented with lots of conditional operators, such as if or switch, that check current state and perform appropriate behavior. Even if you've just heard about state machines for the first time, you had probably implemented it at least once. Does the following code structure look familiar?

class Document
    string state;
    // ...
    method publish() {
        switch (state) {
            "draft":
                state = "moderation";
                break;
            "moderation":
                if (currentUser.role == 'admin')
                    state = "published"
                break;
            "published":
                // Do nothing.
        }
    }
    // ...

The biggest limitation of the state machine build with conditionals will show itself if you add some more states and state-dependent behaviors to the Document class. Most methods will contain monstrous conditionals required for picking the proper behavior according to the state.

Such code is very difficult to maintain since any change to the transition logic requires double checking each conditional in every method.

The problem tends to get bigger the older a project is since it's quite difficult to predict all possible states and transitions beforehand at a design stage. Hence, a lean state machine built with a small set of conditionals can grow into a bloated mess over time.

Solution

The State pattern suggests to create new classes for all possible states of a context object and to extract the state-related behaviors into these classes.

The context will contain a reference to a state object that represents its current state. Instead of performing a behavior on its own, the context will delegate the execution to a state object.

To change the context's state, one would pass another state object to the context. But to make states interchangeable, all states classes must follow the common interface, and the context must communicate with its state object via that interface.

The described structure may look like the Strategy pattern, but there's one key difference. In the State pattern, the context, as wells as particular states, can initiate the transitions from one state to another.

Real-World Analogy

Smartphone

The controls of your smartphone behave differently depending on the current state of the device:

  • When the phone is unlocked, pressing buttons execute various functions.
  • When the phone is locked, all buttons lead to the unlock screen.
  • When the phone's charge is low, all buttons show the charging screen.

Structure

State pattern structure
  1. Context stores a reference to a Concrete State object and delegates it all state-related behaviors. Context works with a state via the common State interface. Context must expose a method for passing it a new state object.

  2. State defines a common interface for all Concrete States. It declares methods that should make sense for all states. But each state will provide a different implementation.

  3. Concrete State implement behaviors related to a particular state. Additional base classes may be created to avoid duplication of similar code across multiple states.

    A state may store the back reference to its context object. This would not only give it the access to context data but also provide a way to initiate state transitions.

  4. Both the Context and Concrete States may decide when to initiate a state transition and which would the following state be. To perform the transition, a new state object should be passed to the context.

Pseudocode

In this example, the State pattern lets the same media player controls behave differently, depending on the current playback state. The main class of the player contains a reference to a state object, which performs most of the work for the player. Some actions may end-up replacing the state object with another, which changes the way the player reacts to the user interactions.

The State pattern lets the player change its behavior without other objects noticing. The transition can be performed either by the player itself or by a particular state object.

// Common interface for all states.
abstract class State is
    field player: Player

    // Context passes itself through the state constructor. This may help a
    // state to fetch some useful context data if needed.
    constructor State(player) is
        this.player = player

    abstract method onLock(event)
    abstract method onPlay(event)
    abstract method onNext(event)
    abstract method onPrevious(event)


// Concrete states provide the special implementation for all interface methods.
class LockedState is
    method onLock(event) is
        if (player.playing)
            player.changeState(new PlayingState(player))
        else
            player.changeState(new ReadyState(player))

    method onPlay(event) is
        Do nothing.

    method onNext(event) is
        Do nothing.

    method onPrevious(event) is
        Do nothing.


// They can also trigger state transitions in the context.
class ReadyState is
    method onLock(event) is
        player.changeState(new LockedState(player))

    method onPlay(event) is
        player.startPlayback();
        player.changeState(new PlayingState(player))

    method onNext(event) is
        player.nextSong();

    method onPrevious(event) is
        player.previousSong();


class PlayingState is
    method onLock(event) is
        player.changeState(new LockedState(player))

    method onPlay(event) is
        player.stopPlayback();
        player.changeState(new ReadyState(player))

    method onNext(event) is
        if (event.doubleclick)
            player.nextSong();
        else
            player.fastForward(5)

    method onPrevious(event) is
        if (event.doubleclick)
            player.previous();
        else
            player.previousSong(5)


// Player acts as a context.
class Player is
    field state: State
    field UI, volume, playlist, currentSong

    constructor Player(player) is
        this.state = new ReadyState(this)
        UI = new UserInterface()

        // Context delegates handling user's input to a state object. Naturally,
        // the outcome will depend on what state is currently active, since all
        // states can handle the input differently.
        UI.lockButton.onClick(state.onLock)
        UI.playButton.onClick(state.onNext)
        UI.nextButton.onClick(state.onNext)
        UI.prevButton.onClick(state.onPrevious)

    // State may call some service methods on the context.
    method startPlayback() is
        // ...
    method stopPlayback() is
        // ...
    method nextSong() is
        // ...
    method previousSong() is
        // ...
    method fastForward(time) is
        // ...
    method rewind(time) is
        // ...

Applicability

When you have an object that behaves differently depending on its current state. The number of states is big. The state-related code changes frequently.

The State pattern suggests isolating the code, related to a state inside a separate class. The original class called "context" should have a reference to one of such state objects. It would delegate the work to a linked state object. Such structure allows changing the context's behavior by supplying it with a different state object.

When a class is polluted with massive conditionals that alter method's behavior according to the current values of the class's fields.

The State pattern turns branches of the conditionals into methods within corresponding state classes. Then it relies on polymorphism to delegate the execution of the behavior to a linked state object.

When you have a lot of duplicate code across similar states and transitions of the conditional-based state machine.

The State pattern allows you to compose hierarchies of state classes and reduce duplication by moving common code into the base classes of the hierarchy.

How to Implement

  1. Decide what class will play the role of Context. This could be an existing class, which already has the state-dependent code; or a new class, if the state-related code is distributed across multiple classes.

  2. Create the State interface. In most cases, it will mirror the methods declared in the Context.

  3. For each actual state, create a class implementing the State interface. Go over the Context methods and extract all code related to the particular state into the appropriate state class.

  4. Add a reference field of the State type to the Context class and a public method to override its value.

  5. Go over the method of the Context once again and replace leftovers of state conditionals with calls to the corresponding methods of the state object.

  6. To switch the state of the Context, one must create an instance of a state class and pass it to the context. This can be done by the Context itself, or by the States, or by the Client. Note that the creator will become dependent on the concrete State class it instantiates.

Pros and Cons

  • Eliminates state machine conditionals.
  • Organizes the code related to particular states into separate classes.
  • Simplifies the code of the context.
  • Can be an overkill if a state machine has only a few states or rarely changes.

Relations with Other Patterns

  • State, Strategy, Bridge (and to some degree Adapter) have similar solution structures. They all share elements of the "handle/body" idiom. They differ in intent - that is, they solve different problems.

  • State can be considered as an extension of the Strategy pattern. Both patterns use the composition to change the behavior of the main object by delegating the work to the helper objects. The Strategy pattern makes these objects completely independent. However, the State pattern allows state objects to alter the current state of the context with another state, making them interdependent.

Implementations in Different Programming Languages

Java