Skip to content
November 10, 2008 / Shrikant Patil

Behavioral Design Patterns: State Design Pattern

The State Design Pattern is used when an application has different states, transitions between states and Objects behavior changes according to the changed state. Each Object has its own state defined internally.

To Understand the State Design Pattern, we need to know some related concepts;
1) State Machines 2) Transitions 3) State Triggers

To understand whole of above concepts and State pattern, let us consider an example of MP3 Player. The player contains at lest the following possible states;
1)    Playing
2)    Paused.
3)    Stopped

Let us go one-by-one
1) State Machines: It is a model of behavior composed of a finite number of states, transitions between those states, and actions. A finite state machine is an abstract model of a machine with a primitive internal memory.

In our case, MP3 Player is State Machine. It contains different states and each state performs a specific action. The MP3 Player needs to maintain the record of current state. Consider the situation, suppose if MP3 Player already in the state of Playing, and you press the Play button of the Player. At that moment it shouldn’t perform any action. So it is important for each state machine to maintain the current state.

2) Transitions: These are the actions which changes the state of state machine. The moment of a state machine from one state to another state is indicated by the transition.
In our case of MP3 Player, once you click on Play button the player changes its state from Stopped to Playing state. This change is known as transition.

3) State Triggers: These are invoker of transitions. The trigger may be any event which results a change in state from one state to another state. The trigger might be any user action or event with state machine (like clicking on Play button), or it might be internal action performed by the state machine itself (Like Player plays next track after completion of first track).

So let is get more into the pattern;
Here is the theoretical definition of the pattern;
The state pattern allows an object to alter its behavior when it’s internal state changes. The Object will appear to change its class.

And here is the UML diagram;
statepatternAs we can see within above diagram; the pattern contains 1) State Interface 2) Context 3) ConcreteStateA and 4) ConcreteStateB class objects.

1) State Interface: This interface defines the signatures of the methods for concreteState objects. In Context object each ConcreteState object is defined as State interface data type to utilize the polymorphism.

package flexScript
{
    public interface State
    {
        function handle():void;
    }
}

2) ConcreteState A & B: These class implements the State interfaces and implements its own implementations for handle () method, whose signature defined in State interface. The ConcreteState object holds an instance reference of Context object. The constructor receives an instance of Context and adds that reference to objects private context reference variable;

package flexScript
{
    public class ConcreteStateA implements State
    {
        private var context:Context;

        public function ConcreteStateA(cntx:Context)
        {
            context = cntx;
        }

        public function handle():void
        {
            trace("handle() method on ConcreteStateA");
        }

    }
}


package flexScript
{
    public class ConcreteStateB implements State
    {
        private var context:Context;
        public function ConcreteStateB(cntx:Context)
        {
            context = cntx;
        }

        public function handle():void
        {
            trace("handle() method on ConcreteStateB");
        }

    }
}

3) Context: This is the state machine class. It contains number of internal states. The triggers for state change either by user interactions or by internal process occur within this class. It contains reference instances of each concereteStateA and concreteStateB. It also holds a reference of current state.

Constructor method of Context class initializes all concreteState classes by passing an instance of self and initializes the currentState reference to default state (either ConcreteStateA or ConcreteStateB).

At request () method implementation (which is get called when a trigger invoker on context), it delegates the call to handle () method of currentState instance. This method is used by main class of the application to delegate the request on State Machine (Context Object).

It also defines two methods setNewState() and getCurrentState(), which are used to set the new state and get the current state of the state machine (Context Object). These methods are used by ConcreteState objects.

It defines two more methods getStateA() and getStateB(), which are responsible to return the respective concreteState instance. These methods are used by concreteState object.

package flexScript
{
    public class Context
    {
        private var stateA:State;
        private var stateB:State;
        private var currentState:State;

        public function Context()
        {
            stateA = new ConcreteStateA(this);
            stateB = new ConcreteStateB(this);
            currentState = stateA;
        }
        public function request():void{
            currentState.handle();
        }
        public function setNewState(state:State):void{
            currentState = state;
        }
        public function getCurrentState():State{
            return currentState;
        }
        public function getStateA():State{
            return stateA;
        }
        public function getStateB():State{
            return stateB;
        }
    }
}

Let us get into some real word application of MP3 Player, so that we can understand the State pattern more closely; we are considering only three states of MP3 Player; Playing, Paused and Stopped states.

1) Create State Interface – IState:
The interface defines the signature of playAudio, PauseAudio and StopAudio methods, so that ConcreteState objects can implement their own implementations for these methods.

package flexScript
{
    public interface IState
    {
        function playAudio():void;
        function pauseAudio():void;
        function StopAudio():void;
    }
}

2) Creating ConcreteState – PlayingState
It receives the instance of Mp3Player (Context) object at constructor and holds that reference within object.
playAudio() – At Playing state, Track is already playing so there is no need to do anything.
pauseAudio() – At Playing State, Track can be paused. So the method sets new state of pausedState on MP3Player instance (Context Object).
stopAudio() – At Playing State, Track can be stopped. So the method sets new state of StoppedState on MP3Player instance (Context Object).

package flexScript
{
    //ConcreteState class implement IState interface
    public class PlayingState implements IState
    {
        //define a instance to hold reference of Context Object (MP3Player)
        private var mp3Player:MP3Player;
        //constructor should accept the context reference instance
        public function PlayingState(player:MP3Player):void{
            trace("=== Playing State ===");
            mp3Player = player;
        }
        //implementation of IState methods
        public function playAudio():void
        {
            trace("Player already Playing");
        }
        public function pauseAudio():void
        {
            trace("PAUSED STATE: ---");
            mp3Player.setNewState(mp3Player.getPausedState());
        }
        public function StopAudio():void
        {
            trace("STOPPED STATE: ---");
            mp3Player.setNewState(mp3Player.getStoppedState());
        }
    }
}

3) Creating ConcreteState – PausedState
It receives the instance of Mp3Player (Context) object at constructor and holds that reference within object.
playAudio() – At Paused state, Track can be played. So the method sets new state of PlayingState on MP3Player instance (Context Object).
pauseAudio() – At Paused State, Track can’t be paused again. So there is no need to do anything.
stopAudio() – At Paused State, Track can be stopped. So the method sets new state of StoppedState on MP3Player instance (Context Object).

package flexScript
{
    //ConcreteState class implement IState interface
    public class PausedState implements IState
    {
        //define a instance to hold reference of Context Object (MP3Player)
        private var mp3Player:MP3Player;
        //constructor should accept the context reference instance
        public function PausedState(player:MP3Player):void{
            trace("=== Paused State ===");
            mp3Player = player;
        }
        //implementation of IState methods
        public function playAudio():void
        {
            trace("PLAYING STATE: ---");
            mp3Player.setNewState(mp3Player.getPlayingState());
        }
        public function pauseAudio():void
        {
            trace("Player is already Paused");
        }
        public function StopAudio():void
        {
            trace("STOPPED STATE: ---");
            mp3Player.setNewState(mp3Player.getStoppedState());
        }
    }
}

4) Creating ConcreteState – StoppedState
It receives the instance of Mp3Player (Context) object at constructor and holds that reference within object.
playAudio() – At Stopped state, Track can be played. So the method sets new state of PlayingState on MP3Player instance (Context Object).
pauseAudio() – At Stopped State, Track can’t be pause. So there is no need to do anything.
stopAudio() – At Stopped State, Track can’t be stopped again. So there is no need to do anything.

package flexScript
{
    //ConcreteState class implement IState interface
    public class StoppedState implements IState
    {
        //define a instance to hold reference of Context Object (MP3Player)
        private var mp3Player:MP3Player;
        //constructor should accept the context reference instance
        public function StoppedState(player:MP3Player):void{
            trace("=== Stopped State ===");
            mp3Player = player;
        }
        //implementation of IState methods
        public function playAudio():void
        {
            trace("PLAYING STATE: ---");
            mp3Player.setNewState(mp3Player.getPlayingState());
        }
        public function pauseAudio():void
        {
            trace("Playing Track can be paused");
        }
        public function StopAudio():void
        {
            trace("Player is already Stopped");
        }
    }
}

5) Creating Context Class  – MP3Player
It holds instances of PlayingState, PausedState and StoppedState along with an instance of current state. Constructor initializes these concreteState objects by passing the parameter of self and current state is set to default StoppedState.
The methods, playAudio(), pauseAudio() and StopAudio(), delegates the call to currentState’s respective method. setNewState() and getState() methods are used to set new state and get current state of M3Player. Methods like getPlayingState() , getPausedState() and getStoppedState() are used to return the respective concreteState instances.

package flexScript
{
    public class MP3Player
    {
        //declear reference instances to hold all state objects
        private var playingState:IState;
        private var pausedState:IState;
        private var stoppedState:IState;
        //define a reference to hold current state of player
        private var currentState:IState;

        public function MP3Player()
        {
            //initilize all referene state instances by passing MP3 Player
            //object as paramater
            playingState = new PlayingState(this);
            pausedState = new PausedState(this);
            stoppedState = new StoppedState(this);
            //Initially the current state will be stopped
            currentState = stoppedState;
        }
        //define the method for play State
        public function playAudio():void{
            //delegate the call to current state's playAudio method
            currentState.playAudio();
        }
        //define the method for pause State
        public function pauseAudio():void{
            //delegate the call to current state's PauseAudio method
            currentState.pauseAudio();
        }
        //define the method for stop State
        public function StopAudio():void{
            //delegate the call to current state's StopAudio method
            currentState.StopAudio();
        }
        //define a method which accepts a new state 
        public function setNewState(state:IState):void{
            trace("--- State Changed From "+currentState+" to "+state);
            //update the current state
            currentState = state;
        }
        //define a method which return current state of the player
        public function getState():IState{
            return currentState;
        }
        //define a method which retruns PlayingState reference instance
        public function getPlayingState():IState{
            return playingState;
        }
        //define a method which retruns PausedState reference instance
        public function getPausedState():IState{
            return pausedState;
        }
        //define a method which retruns StoppedState reference instance
        public function getStoppedState():IState{
            return stoppedState;
        }

    }
}

6) Creating Client:
Our client is simple MXML file. It displays three buttons Play, Pause and Stop. Once the application starts it creates an instance of MP3Player. Each button click calls respective methods which invoke respective methods on Mp3Player (Context Object).

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute" creationComplete="init()" height="42" width="199">
<mx:Script>
    <!&#91;CDATA&#91;
        import flexScript.MP3Player;

        private var audioPlayer:MP3Player;
        private function init():void{
            audioPlayer = new MP3Player();
        }

        private function playTrack():void{
            audioPlayer.playAudio();
        }
        private function pauseTrack():void{
            audioPlayer.pauseAudio();
        }
        private function stopTrack():void{
            audioPlayer.StopAudio();
        }
    &#93;&#93;>
</mx:Script>
    <mx:Button x="10" y="10" label="Play" cornerRadius="0"
     click="playTrack()"/>
    <mx:Button x="69" y="10" label="Pause"  cornerRadius="0"
     click="pauseTrack()"/>
    <mx:Button x="138" y="10" label="Stop" cornerRadius="0"
     click="stopTrack()"/>
</mx:Application>

Please check the application in Debug mode, because the output are done using trace() method.

Advertisements

2 Comments

Leave a Comment
  1. parker / Dec 12 2009 2:55 am

    Love the detail given to full illustration on this one. Very illuminating and just what I was looking for.

Trackbacks

  1. » Behavioral Design Patterns: State Design Pattern

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: