Patterns. The State pattern. Structure. Implementation in C++

The State pattern. Structure. Implementation in C++


Contents


1. General information. Purpose. Class diagram

The State pattern refers to object behavior patterns and is intended to place object states into separate objects. Depending on the internal state, the object changes its behavior.

Figure 1 shows the structure of the State pattern.

The structure of State pattern

Figure 1. The structure of State pattern

 

2. Participants of the pattern

According to Figure 1, the pattern participants are:

  • Client – a client that uses different states of an object through a context;
  • Context – context class. This is the class through which the client has access to the specific state of an object;
  • State – state class. Defining an interface to encapsulate the behavior of an object. This behavior depends on the specific state of the Context;
  • ConcreteStateA, ConcreteStateB – subclasses that implement the appropriate behavior associated with various states of the Context.

 

3. Relations

For the State pattern, the following relationships can be distinguished (see Figure 1):

  • using the state field, the Context class delegates requests to the current object. These queries depend on the selected state;
  • It is possible to pass a Context object as an argument to a State object. In this case, the state object can access the context;
  • the main interface for the client is the context. You can implement context configuration by clients with concrete state objects. You can then conveniently use these state objects;
  • state change can occur in two ways: through the context (Context) or through subclasses of concrete states (ConcreteState).

 

4. Results

The State pattern gives the following results.

  1. The behavior of an object, which depends on the state, is localized and divided into appropriate parts. Each part is a separate state of the object. New states are added by inheriting new subclasses.

The advantage of the State pattern is that you do not need to use conditional statements to define a particular state. Instead, distribution is proposed between subclasses of the State class. However, a negative phenomenon here may be an increase in the total number of classes.

  1. Entering separate objects for different states makes transitions between states more obvious. These transitions represent atomic actions. To implement the transition, you need to change the value of only one object variable state in the Context class.
  1. It is possible to separate objects of the state. In the State object, a particular state can be encoded with the corresponding type (class). Then different contexts can share the same State object. In this case, the states are adaptable.

 

5. Uses of the State pattern

The State pattern should be used in the following cases:

  • when during execution it is necessary to change the behavior of an object depending on its state;
  • when the code contains many branches using conditional statements (selection statements) and each branch is the state of an object. In this case, each branch is placed in a separate class, in which each state is treated as a separate independent object.

 

6. Example pattern implementation in C++

 

// Pattern State
#include <iostream>
using namespace std;

// An interface that encapsulates behavior
// that corresponds to a particular context state (Context)
class State abstract
{
public:
  virtual void Handle() = 0;
};

// Concrete states (behaviour)
class ConcreteStateA : public State
{
public:
  void Handle() override
  {
    cout << "ConcreteStateA::Handle()" << endl;
  }
};

class ConcreteStateB : public State
{
public:
  void Handle() override
  {
    cout << "ConcreteStateB::Handle()" << endl;
  }
};

// Class of the context
class Context
{
private:
  State* state = nullptr;

public:
  // Constructor
  Context(State* state = nullptr) : state(state)
  { }

  void Request()
  {
    state->Handle();
  }
};

void main()
{
  // Client code
  // 1. Create a concrete state
  State* state1 = new ConcreteStateA();
  State* state2 = new ConcreteStateB();

  // 2. Create a context
  Context* context = nullptr;

  // 3. Create the menu of state selection
  int stateNum;
  cout << "stateNum = ";
  cin >> stateNum;

  // 4. Create the state of object
  if (stateNum == 1)
    context = new Context(state1);
  else
    context = new Context(state2);

  // 5. Display the state value
  context->Request();

  // 6. Free memory
  if (state1)
    delete state1;
  if (state2)
    delete state2;
  if (context)
    delete context;
}

 

7. Implementation

When implementing the State pattern, the following questions arise.

  1. There is no clear information about who implements the transition between states. This could be a context, or it could be subclasses of the State class. In this case, a more flexible approach is to use state subclasses, for which the corresponding interface must be implemented.
  1. To represent changes in states, you can use tables. These tables map input data to transitions between states. Using this method, it is determined which state to go to when receiving some input data. Thus, virtual functions are replaced with table lookups.
  1. Selecting the ability to create (destroy) objects during the development process. Objects can be created as needed and at the beginning. In the first method, objects are destroyed immediately after use. In the second method, objects are created at the beginning of the program and destroyed at the very end when the program terminates.

The first option is considered preferable in cases where it is not known what state the system will end up in. Here, some objects may not be used at all, but resources have been allocated for their creation.

The second method is preferable in cases where state changes occur frequently. This saves time on repeated creation/destruction of an object.

  1. Use of dynamic inheritance. This assumes behavior changes on demand (the class of the object changes when executed). But some programming languages do not support this option.

 


Related topics