Click here to Skip to main content
Click here to Skip to main content
Go to top

Understanding State Pattern in C++

, 25 Jun 2006
Rate this:
Please Sign up or sign in to vote.
A very simple way to understand State Design Pattern

Introduction

State pattern is a behavioral type design pattern which is widely used in different applications especially in 3D-Graphics applications and applications for devices. In object oriented design, object can change its behavior based on its current state. A state pattern design implements state and behavior of an object. By using state pattern, we can reduce the complexity in handling different states of an object. This will help us to easily maintain code in future.

Legacy Method of Handling State of an Object

There are a lot of applications which need to maintain different states on different contexts. Mainly, it comes in the matter of 3D-Applications and devices. Without using state pattern, normally we are handling the state as follows:

class MyDevice
{
    int m_nCurrentState;

public:
    MyDevice()
    {
        m_nCurrentState= STATE_OFF;
    }
    // Function handling the state changes
    void HandleState();
};

void MyDevice::HandleState()
{
    /* changing state depends on the current state */
   if(STATE_OFF == m_nCurrentState)
   {
       m_nCurrentState = STATE_ON;
   }
   else if(STATE_ON == m_nCurrentState)
   {
       m_nCurrentState = STATE_ACQUIRING;
   }
   else if(STATE_ACQUIRING == m_nCurrentState)
   {
       m_nCurrentState = STATE_SAVE;
   }
  ....
  ....
  // We can add more states here
}

The problem with the above code is, usually the state changing and other activities will be centralized, so that the main function doing these activities will get complex and lengthy if there are many states are to be handled. This function will also get complex in the matter of maintainability. For handling few number of states, this approach is most suitable and easy to handle. The state pattern comes when complexity matters.

The Concept Behind the State Pattern

In state pattern, an object can have different states. Each state knows what its next state is. By using this architecture, we can add or remove a state at ease. This architecture mainly consists of 3 types of classes:

  1. Context Class: This class is the one which has the states. The application will be using (interfering with) this class. The class is responsible to maintain current state. 
  2. State Class: This is an abstract base class which can hold the different states (sub classes). The context class will be using this class pointer as its member to point the current state.
  3. Concentrate Class: This class implements the behavior of the state and behavior of the object.

Example: State Change

Here I’m taking the Sun as example: A person who is living in earth can view the sun in different states (even it remains constant). Those are morning, noon, afternoon, evening, night. For the sake of simplicity, I’m taking 3 states: Morning, Evening and Night.
The Sun starts (Initial state) with state Morning.
Morning knows that sun will move to Evening after its time period, Evening knows that beautiful Night will come after its turn and finally after a dark Night, it knows that Sun will change to a cool Morning. In this architecture, each state has a next state. When the object receives a command to change its state, it will ask the current state, what its next state is and modify itself to the new state returned.

Adding a New State

Suppose we want to add a new state Noon for Sun. We can do in simple steps as described below. Take Noon as an example:

  1. Define a new concentrate class (Noon class)
  2. Define its next state (Evening)
  3. Give this state as the next state of another state (change Morning’s next state from Evening to Noon).

Code

State Class

class CBaseState
{
public:
    // Pure virtual function
    virtual CBaseState* GetNextState() = 0;
    // print the string
    virtual    char* ToString() = 0;
};

Concentrate Classes

//////////////////////////////////////////////////////////////////////////
// State Morning
//////////////////////////////////////////////////////////////////////////
class CMorning : public CBaseState
{
public:
    virtual CBaseState* GetNextState();
    virtual    char* ToString();
};

//////////////////////////////////////////////////////////////////////////
// State Evening
//////////////////////////////////////////////////////////////////////////
class CEvening : public CBaseState
{
public:
    virtual CBaseState* GetNextState();
    virtual    char* ToString();
};

//////////////////////////////////////////////////////////////////////////
// State night
//////////////////////////////////////////////////////////////////////////
class CNight: public CBaseState
{
public:
    virtual CBaseState* GetNextState();
    virtual    char* ToString();
};

Context Class

//////////////////////////////////////////////////////////////////////////
// Context Class
//////////////////////////////////////////////////////////////////////////

class CSun
{
public:
    CSun();
    CSun(CBaseState* pContext /* Pass Allocated memory */);
    ~CSun();
    // Handles the next state
    void StateChanged();
    char* GetStateName();
protected:
    void DoCleanUp();
    // Pointer which holds the current state
    // Since this is and base class pointer
    // of Concentrate classes, it can holds their objects
    CBaseState* m_pState;
};

When State Change Request Comes

In the above example, the sun will be initialized to any of the states, for e.g., say morning.

CSun objSun(new CMorning);

If we need to change its current state to the next state, it is possible to do so by calling StateChanged interface provided by the context class.
See the sample code snippet for initializing and changing the state:

CSun objSun(new CMorning);
printf("\n\nSun Says Good %s !!!",objSun.GetStateName());
// inform that state has been changed
objSun.StateChanged();
printf("\n\nSun Says Good %s !!!",objSun.GetStateName());
// inform that state has been changed
objSun.StateChanged();
printf("\n\nSun Says Good %s !!!",objSun.GetStateName());
// inform that state has been changed
objSun.StateChanged();	
printf("\n\nSun Says Good %s !!!",objSun.GetStateName());

Inside the interface, the following things are happening:

// Handles the state change
void CSun::StateChanged()
{
    if (m_pState)
    {
        // Getting Next State
        CBaseState* pState = m_pState->GetNextState();
        // de allocates the memory
        delete m_pState;
          m_pState = pState;
    }
}

Defining Next State

Calling the GetNextState virtual function depends on the object. Each object will return its next state.

Morning object’s next state:

CBaseState* CMorning::GetNextState()
{
    return new CEvening;
}

Evening object’s next state:

CBaseState* CEvening::GetNextState()
{
    return new CNight;
}

Night object’s next state:

CBaseState* CNight::GetNextState()
{
    return new CMorning;
}

What About This Code

The above described code is a very basic level implementation of state Pattern. You can add more interfaces for your class to manage the state (e.g. setting and getting state) and improve the code with a good memory handling strategy.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Sarath C
Technical Lead
India India
Software Developer
Follow on   Twitter

Comments and Discussions

 
GeneralState Pattern Example Code Pinmemberbenbrock266-Apr-09 3:39 
GeneralRe: State Pattern Example Code PinmemberDenWhin29-Apr-10 7:13 
GeneralRe: State Pattern Example Code PinmemberSarath.29-Apr-10 17:43 
GeneralRe: State Pattern Example Code Pinmemberfpersson.se10-Dec-11 1:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140916.1 | Last Updated 25 Jun 2006
Article Copyright 2006 by Sarath C
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid