State design pattern allows an object to change its behaviour whenever its state changes. This design pattern is very similar to state machines in which behaviour of an object based on an event is dependent on the its state. This design pattern makes the software easily extensible as adding new states are quite easy because it doesn’t affect the behaviour of existing states.
Before going ahead have a look at Design pattern simplified version.
Generic model class diagram of State design pattern is as shown in image.
This design pattern allows to define an object known as State, which encapsulates behaviour of each state. It also provide interfaces to execute state change events. This allows the actual class to be independent of state specific behaviour.
State Design Pattern Code Example:
Let’s take an example of machines which are being present all around us. These are perfect example of State design pattern. A Light switch can have two states On and Off. It can have two events TurnOn and TurnOff. Let’s try to solve this problem using state design pattern.
#include <iostream>
#include <unistd.h>
using namespace std;
class Equipment;
class State
{
public:
virtual void on (Equipment *e) = 0;
virtual void off (Equipment *e) = 0;
};
class SwitchedOnState: public State
{
public:
SwitchedOnState ()
{
cout << "Light turned on"<< endl;
}
virtual void on (Equipment *e)
{
cout << "Light is already On, No Action needed" << endl;
}
virtual void off (Equipment *e);
};
class SwitchedOffState: public State
{
public:
SwitchedOffState ()
{
cout << "Light turned off"<< endl;
}
virtual void off (Equipment *e)
{
cout << "Light is already Off, No Action needed" << endl;
}
virtual void on (Equipment *e);
};
class Equipment
{
public:
virtual void set_state (State *st) = 0;
virtual void turn_on () = 0;
virtual void turn_off () = 0;
};
class LightSwitch: public Equipment
{
private:
State *st;
public:
LightSwitch ()
{
st = new SwitchedOffState ();
}
virtual void set_state (State *s)
{
st = s;
}
virtual void turn_on ()
{
st->on (this);
}
virtual void turn_off ()
{
st->off (this);
}
~LightSwitch ()
{
delete st;
}
};
void SwitchedOffState::on (Equipment *e)
{
cout << "Switching light on !!!" << endl;
e->set_state (new SwitchedOnState ());
delete this;
}
void SwitchedOnState::off (Equipment *e)
{
cout << "Switching light off !!!" << endl;
e->set_state (new SwitchedOffState ());
delete this;
}
int main ()
{
LightSwitch ls;
ls.turn_on ();
ls.turn_off ();
ls.turn_off ();
ls.turn_on ();
}
Output of above program:
Light turned off
Switching light on !!!
Light turned on
Switching light off !!!
Light turned off
Light is already Off, No Action needed
Switching light on !!!
Light turned on
Advantages:
- Code will become simpler as State handling is moved into specific states classes.
- Adding new states are comparatively easy.
Disadvantages:
- Number of state class and state transition function to be implemented, increases exponentially with larger number of states.