BlackWasp
Design Patterns
.NET 1.1+

State Design Pattern

The state pattern is a design pattern that allows an object to completely change its behaviour depending upon its current internal state. By substituting classes within a defined context, the state object appears to change its type at run-time.

What is the State Pattern?

The state pattern is a Gang of Four design pattern. This is a behavioural pattern as it defines a manner for controlling communication between classes or entities. The state pattern is used to alter the behaviour of an object as its internal state changes. The pattern allows the class for an object to change at run-time without changing the interface used to access the object or losing the current state. The class change is hidden to the outside world with the use of a wrapper object, or context.

The state pattern is useful when creating object-oriented state machines, where the functionality of an object changes fundamentally according to its state. By using multiple concrete classes, each inheriting from the same base class, large differences in functionality are possible without resorting to numerous "if" or "switch" statements.

An example of the state design pattern could be used for the control panel of a simple media player. When the device is playing MP3 music, the "play" button could pause and restart the music. When the user is listening to the radio, the same button may search for the next radio station. To switch between the states of MP3 playback, radio tuning and standby mode, the user could press the "audio source" button.

Implementing the State Pattern

State Design Pattern UML

The UML class diagram above describes an implementation of the state design pattern. The items in the diagram are described below:

  • Context. The Context class is used by clients of the state design pattern. Clients do not access the state objects directly. The Context class holds a concrete state object that provides the behaviour according to its current state.
  • StateBase. This abstract class is the base class for all concrete state classes. StateBase defines the interface that will be used by the Context object to access the changeable functionality. No state, in terms of fields or properties, is defined in the StateBase class or its subclasses.
  • ConcreteState A/B. The concrete state classes provide the real functionality that will be used by the Context object. Each state class provides behaviour that is applicable to a single state of the Context object. They may also include instructions that cause the Context to change its state.

The following shows the basic code of the state design pattern implemented using C#. Note that the starting state is passed to the Context object via a constructor. As calls are made to the Request method they are routed to the current State object's Handle method. This outputs a message and switches the state of the Context.

public class Context
{
    private StateBase _state;

    public Context(StateBase state)
    {
        _state = state;
    }

    public void Request()
    {
        _state.Handle(this);
    }

    public StateBase State
    {
        get { return _state; }
        set { _state = value; }
    }
}


public abstract class StateBase
{
    public abstract void Handle(Context context);
}


public class ConcreteStateA : StateBase
{
    public override void Handle(Context context)
    {
        Console.WriteLine("Handle called from ConcreteStateA");
        context.State = new ConcreteStateB();
    }
}


public class ConcreteStateB : StateBase
{
    public override void Handle(Context context)
    {
        Console.WriteLine("Handle called from ConcreteStateB");
        context.State = new ConcreteStateA();
    }
}

Example State

In this section we will create a simple example of the state design pattern using C#. This example will simulate the media player described earlier in the article. The two buttons on the media player, "Play" and "Audio Source", will be represented by methods in the state base class and its subclasses.

The example media player will have four possible states. The states will be "MP3 Playing", "MP3 Paused", "Radio" and "Standby" and each will be represented by one of four concrete state classes.

The behaviour of the two buttons will vary according to the currently selected state. The actions for each button will be as follows:

StatePlay ButtonAudio Source Button
StandbyNo effect.Switch state to "MP3 Playing".
MP3 PlayingStop music, switch state to "MP3 Paused".Switch state to "Radio".
MP3 PausedStart music, switch state to "MP3 Playing".Switch state to "Radio".
RadioTune next radio station.Switch state to "Standby".

The skeleton code for the audio player is as follows. NB: The Context in this example is provided by the AudioPlayer class.

public class AudioPlayer
{
    private AudioPlayerState _state;

    public AudioPlayer(AudioPlayerState state)
    {
        _state = state;
    }

    public void PressPlay()
    {
        _state.PressPlay(this);
    }

    public void PressAudioSource()
    {
        _state.PressAudioSource(this);
    }

    public AudioPlayerState CurrentState
    {
        get { return _state; }
        set { _state = value; }
    }
}


public abstract class AudioPlayerState
{
    public abstract void PressPlay(AudioPlayer player);

    public abstract void PressAudioSource(AudioPlayer player);
}


public class StandbyState : AudioPlayerState
{
    public StandbyState()
    {
        Console.WriteLine("STANDBY");
    }

    public override void PressPlay(AudioPlayer player)
    {
        Console.WriteLine("Play pressed: No effect");
    }

    public override void PressAudioSource(AudioPlayer player)
    {
        player.CurrentState = new MP3PlayingState();
    }
}


public class MP3PlayingState : AudioPlayerState
{
    public MP3PlayingState()
    {
        Console.WriteLine("MP3 PLAYING");
    }

    public override void PressPlay(AudioPlayer player)
    {
        player.CurrentState = new MP3PausedState();
    }

    public override void PressAudioSource(AudioPlayer player)
    {
        player.CurrentState = new RadioState();
    }
}


public class MP3PausedState : AudioPlayerState
{
    public MP3PausedState()
    {
        Console.WriteLine("MP3 PAUSED");
    }

    public override void PressPlay(AudioPlayer player)
    {
        player.CurrentState = new MP3PlayingState();
    }

    public override void PressAudioSource(AudioPlayer player)
    {
        player.CurrentState = new RadioState();
    }
}


public class RadioState : AudioPlayerState
{
    public RadioState()
    {
        Console.WriteLine("RADIO");
    }

    public override void PressPlay(AudioPlayer player)
    {
        Console.WriteLine("Play pressed: New station selected");
    }

    public override void PressAudioSource(AudioPlayer player)
    {
        player.CurrentState = new StandbyState();
    }
}

Testing the State

To test the state machine for the audio player and to see the elegance of the simple interface it presents, execute the following code within a console application's Main method.

AudioPlayer player = new AudioPlayer(new StandbyState());
player.PressPlay();
player.PressAudioSource();
player.PressPlay();
player.PressPlay();
player.PressAudioSource();
player.PressPlay();
player.PressAudioSource();

/* OUTPUT

STANDBY
Play pressed: No effect
MP3 PLAYING
MP3 PAUSED
MP3 PLAYING
RADIO
Play pressed: New station selected
STANDBY

*/
Link to this Page21 July 2009
TwitterTwitter RSS Feed RSS