The State pattern. Structure. Implementation in C++
Contents
- 1. General information. Purpose. Class diagram
- 2. Participants of the pattern
- 3. Relations
- 4. Results
- 5. Uses of the State pattern
- 6. Example pattern implementation in C++
- 7. Implementation
- Related topics
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.
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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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
- Behavior patterns. Overview
- The Chain of Responsibility pattern. General information. Implementation in C++
- Command. Implementation in C++
- General information. Methods of implementation. Structural scheme. Example on C++
- The Template Method pattern. Implementation in C++
⇑