State design pattern for beginners.

Background –

There is a very thin line difference between command design pattern and state design pattern, if you look at them from a very high level, they may look alike. Once you read this blog, you will be able to differentiate between them.

State design pattern observe the states of the object. We will have different states of objects during execution, each state has its unique behavior. So if we watch them closely. We will understand that it is a 1 to 1 mapping between different states and their responses. 

Let me explain it through an example – think of a class who is having 3 bool data members. If I ask you how many different state (or say behavior) it can have. The answer is simple – 8, see below –



It means in general, if we have n number of bool data members, then we can have 2^n different states. Or say 2^n different behaviors. In general, we have more complex data members then bool, hence complexity increases exponentially.

Why we need state design pattern –

Above mentioned example was tiny, think of a controller system of nuclear power plants. Where we have more than 100 parameters to set many different behaviors, so you will have more than 2^100 different states to deal with.

We have added more complexity here, how to address that? Any controller class, who has to handle these many states. And divert the code flow according to change in parameter of plant will become a challenging class. That class may become the buggiest part of your system. And full understanding of such class will become “the task.” for any developer.

Now think, in coming future, I have added one more parameter in our plant controlling system. And now, the controller has to update the logic. There will be so much difficulty and substantial code changes required, which has to go through regression testing. Whenever I add a new parameter. This is not acceptable. It will increase maintenance costs.

But through state design pattern, we can eliminate complexity.   

State design pattern –

We create different class per state. One class will be responsible for one state of the object. If we added new parameters into the system, we will create all together a new classes instead of disturbing our old written classes. We will reduce testing costs and will follow the open-close design principle as well.

Think about an ATM machine, you would have used it so many times. One ATM machine is like one existing object, now how it will behave, will depend on the states. For example –
           1.       No card inserted – machine will act static with no operation.
           2.       You inserted the card – state of machine has changed, it will show some options.
           3.       You asked for a pin number.
           4.       You inserted a pin, if correct, again state of ATM get changed.
           5.       You withdraw money.
     Etc….

There are many states an ATM has (but finite number of states, it could be many, but limited). All different states are different behaviors and have its own functionality to execute. Withdraw is a behavior or state of ATM, and it has to perform certain operations to get that work done. We write them into a different class in state design pattern.

So depending upon the internal state, we change the behavior! Simple :)

Difference between command design pattern and state design pattern –

      1.       When request is based on transaction-based system, then we use command design pattern (for example – in net banking 3rd party transaction is one transaction, checking balance is another transaction, closing account is another transaction..etc.).

      2.       When there is no transaction/business logic, you have to create an object (with default settings) and change its behavior as an internal state of the object changes, then we use state design pattern. For example, any controller system of a plant, airport, monitoring system, nuclear power plant, etc. More specifically, if the heat of some machine in plant crosses threshold limit, then automatically switch on the fan (same applied in laptops as well).

UML Structure –

state DP
UML structure of state design pattern



C++ Example –

Example of State DP
Example of State design pattern (Click on picture to see zoom view)



#include<iostream>
using namespace std;

class Plant;

typedef enum _STATE
{
  ON,
  OFF
} STATE;
class IState
{
public:
  virtual STATE GetState() = 0;
  virtual void SwitchOnFan(Plant* machine) { ; }
  virtual void SwitchOffFan(Plant* machine) { ; }
};

class IOnState : public IState
{
  virtual void SwitchOnFan(Plant* machine) = 0;
  STATE GetState() = 0;
};

class OnFan : public IOnState
{
public:
  void SwitchOnFan(Plant* machine);
  inline STATE GetState() { return ON; }
};

class IOffState : public IState
{
  virtual void SwitchOffFan(Plant* machine) = 0;
  inline STATE GetState() = 0;
};

class OffFan : public IOffState
{
  void SwitchOffFan(Plant* machine);
  inline STATE GetState() { return OFF; }
};

IState* On();
IState* Off();
class Plant
{
 private:
   IState *pIState;

 public:
    void SwitchOnFan()
    {
      if (pIState->GetState() == ON)
      {
        cout << "Fan is already running" << endl;
      }
      else
      {
        On()->SwitchOnFan(this);
      }
    }
    void SwitchOffFan()
    {
      if (pIState->GetState() == OFF)
      {
        cout << "Fan is already off" << endl;
      }
      else
      {
        Off()->SwitchOffFan(this);
      }
    }
    void SetState(IState* state)
    {
      pIState = state;
    }
    explicit Plant()
    {
      pIState = Off();
    }
};

void OnFan::SwitchOnFan(Plant* machine)
{
  cout << "Switching on fan" << endl;
  machine->SetState(this);
}
void OffFan::SwitchOffFan(Plant* machine)
{
  cout << "Switching Off fan" << endl;
  machine->SetState(this);
}
namespace
{
  IState* pOn   = new OnFan();
  IState* pOff  = new OffFan();
}
IState* On()
{
  return pOn;
}
IState* Off()
{
  return pOff;
}
void Setup()
{
  On();
  Off();
}
/*
#######################################################################################
#########  __  __       _        ######################################################
######### |  \/  | __ _(_)_ __   ######################################################
######### | |\/| |/ _` | | '_ \  ######################################################
######### | |  | | (_| | | | | | ######################################################
######### |_|  |_|\__,_|_|_| |_| ######################################################
#######################################################################################
*/
int main(int argc, char** argv) 
{
  Setup();
 Plant objPlant;
 int temperature;
 do
  {
    cout<<endl<<"Enter the temperature in degree Celsius - ";
    cin>>temperature;
    if(temperature > 40) 
     objPlant.SwitchOnFan();
    else 
      objPlant.SwitchOffFan();
    cout<<endl<<"enter -1 for exit any other number to continue - ";
    cin>>temperature;
 } while(temperature != -1);
 return 0;
}

///////////////////////////////////////////////////////////////////-------------OutPut
/*
Enter the temperature in degree Celsius - 25
Fan is already off

enter -1 for exit any other number to continue - 1

Enter the temperature in degree Celsius - 45
Switching on fan

enter -1 for exit any other number to continue - 1

Enter the temperature in degree Celsius - 25
Switching Off fan
*/
Thanks for reading it. To learn more about design patterns and basic design principles, please see my web page.

Comments

Popular posts from this blog

Non-virtual interface idiom (NVI)

Architectural patterns => Mud to structure => layers.

Architectural style -> Adoptable system -> Reflection.