Chapter 19 : Series On Design Patterns – State Pattern

Key Words: State design pattern

Topics at a glance:

  • Delegating state manipulation using state objects.
  • Let us turn on a bulb!

State design pattern

In this chapter we will see another behavioral pattern – the state pattern. We have all heard about Finite State Machines (FSM). FSM abstracts the behavior of the machine into identifiable unique entities called states. At any point of time, an FSM will be in a unique state and will exhibit behavior appropriate to that state. FSM can change from one state to another state upon the trigger of a specific event/condition.

In state pattern, we basically split the state and the machine. It is more correct to say that a machine is at a state rather than a machine has a state. In state pattern we make a State class as a base class and make each state the machine support into separate derived classes. Machine as said before, is always at a state in any given context. That said, a machine here, can be treated as a context class which will be at a state at any point of time. Machine will have a reference (pointer) to its unique state object that identifies/realizes its actual state, in that context. Machine will implement certain interfaces to manipulate its state for the clients. Machine will delegate the actual state manipulation to the state object. State object instance manages the state by executing the set of actions required to be performed in that state and also implements the necessary functionality to transition to another state as required.

For demonstrating an actual state pattern implementation, I am taking an example of a light bulb that can be turned on and off. Here the machine is defined by the Bulb class and it’s identifiable states are On_State class and Off_State class; State class being the abstract base class that machine will use to manipulate its state.

Let us see this in action.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#include <iostream>
#include <string>


using namespace std;

class Bulb;

class State
{
public:
    State(){}
    virtual ~State(){}
    virtual void on(Bulb *bulb);
    virtual void off(Bulb *bulb);
};

class Bulb
{
private:
    State *current;
public:
    Bulb(State *state) : current{state} {}
    ~Bulb(){}
    
    void set_current_state(State *state)
    {
        current = state;
    }
    
    void on()
    {
        current->on(this);
    }
    
    void off()
    {
        current->off(this);
    }
    
};


void State::on(Bulb *bulb)
{
    cout << "already in on state" << endl;
    return;
}

void State::off(Bulb *bulb)
{
    cout << "already in off state" << endl;
    return;
}


class On_State : public State
{
public:
    On_State(){}
    ~On_State(){}
    
    void on(Bulb *bulb);
    void off(Bulb *bulb);
};

class Off_State : public State
{
public:
    Off_State(){}
    ~Off_State(){}
    
    void on(Bulb *bulb);
    void off(Bulb *bulb);
};

void On_State::on(Bulb *bulb)
{
    // nothing to implement
    // just delegate this to base class 
    State::on(bulb);
}

void On_State::off(Bulb *bulb)
{
    // create an Off_State instance
    Off_State *new_state = new Off_State();
    bulb->set_current_state(new_state);
    cout << "Bulb is switched off" << endl;
    
    delete this;
}

void Off_State::on(Bulb *bulb)
{    
    // create an Off_State instance
    On_State *new_state = new On_State();
    bulb->set_current_state(new_state);
    cout << "Bulb is switched on" << endl;
    
    delete this;
}

void Off_State::off(Bulb *bulb)
{
    // nothing to implement
    // just delegate this to base class 
    State::off(bulb);
    
    return;
}

int main()
{
    string choice;
    
    Bulb b1(new Off_State());
    
    cout << "Bulb controller application" << endl;
    
    while(true)
    {
        choice.clear();
        cout << "What to do next : ON, OFF, EXIT" << endl;
        cin >> choice;
        
        if(0 == choice.compare("ON"))
        {
            b1.on();
        }
        else if(0 == choice.compare("OFF"))
        {
            b1.off();
        }
        else if(0 == choice.compare("EXIT"))
        {
            cout << "Exiting bulb controller application" << endl;
            break;
        }
    }
    
    return 0;
}

Want to see the result?

Bulb controller application
What to do next : ON, OFF, EXIT
ON
Bulb is switched on
What to do next : ON, OFF, EXIT
OFF
Bulb is switched off
What to do next : ON, OFF, EXIT
OFF
already in off state
What to do next : ON, OFF, EXIT
ON
Bulb is switched on
What to do next : ON, OFF, EXIT
ON
already in on state
What to do next : ON, OFF, EXIT
OFF
Bulb is switched off
What to do next : ON, OFF, EXIT
EXIT
Exiting bulb controller application

Here, one thing to note is that, when the bulb is in ON state, a command to switch it on, will result in an error. Similarly, when bulb is in OFF state, you cannot turn it off again. i.e. On_State will only define the functionality to transition to OFF state, whereas Off_State defines the functionality to transition to ON state. Machine itself doesn’t need to track in which state it is currently at, in its course of execution. That said, using state pattern, we have separated the state and the machine.

Please go through the next chapter, which will improvise this Bulb controller application using bridge pattern.

Enjoyed the chapter? Let me know in the comments below. Thanks 😊

0

Leave a Reply