The C++ State Pattern for Network Operations
Computer networks are odd things! They usually just sit passively awaiting traffic, and when traffic arrives it gets forwarded as quickly as possible.
You might say that networks have a relatively small number of states—the focus of networking is mostly in remaining operational and providing wide coverage. However, in spite of its apparent simplicity, state is still very important in networking.
In this article, I use the state design pattern to describe a simple C++ solution for managing state in networking. The C++ code used is based on an earlier article (see reference [1] in the "Additional Reading" section at the end of this chapter) and serves to illustrate the flexibility of the design pattern and the reusability of the associated code. You can take two things from this article:
- Proof of the flexibility of the state pattern
- An example of code reuse
The specific application of interest is that of modeling a forwarding engine, which is a piece of software that resides inside a network device, the purpose of which is to handle the data transfer for the device.
The forwarding engine is represented here as an entity with the following states:
- Initial state— device is ready but can’t forward data
- Forwarding state— device can now forward data
- Exit state— device is ready, can’t forward data, but can shut down if required
- Engine termination
Each state presents a finite set of capabilities, which is a key aspect of the state pattern. Do you ever find yourself writing long series of if-else statements? It’s possible that the state design pattern might provide a more elegant solution.
So instead of trying to write one big C++ handler to model all possible states, a number of small dedicated states are provided.
Listing 1 shows the overall solution in which
- I create a forwarding engine entity in its initial state.
- I determine and print the state-specific capabilities of the engine.
- I then switch between the relevant states, printing the state-specific capabilities.
- I delete the engine object and exit the program.
Listing 1 Implementation of the state pattern
EngineEntity* myEngineEntity = new EngineEntity(); printf(myEngineEntity->getStateCapabilities()); printf("ENGINE STATE: Initial state\n"); printf("ENGINE STATE TRANSITION ->: Moving to the forwarding state\n\n"); myEngineEntity->changeState(ForwardingState::Instance()); printf(myEngineEntity->getStateCapabilities()); printf("ENGINE STATE TRANSITION ->: Moving to the exit state\n\n"); myEngineEntity->changeState(ExitState::Instance()); printf(myEngineEntity->getStateCapabilities());
As Listing 1 shows, modeling the forwarding entity as a multistate engine provides the following benefits:
- I can interrogate the state-specific engine capabilities.
- I can easily change states when I need other capabilities.
The key benefit of the state pattern is a more natural mapping between states and the associated capabilities. This is an example of the principle of separation of concerns. The concerns represent the major elements or features of a software entity. (Examples of software concerns include logging, security, and so on.)
Many applications (too many, in fact) implement their major concerns in a monolithic fashion, which can make later changes very difficult. The state pattern helps provide separation of concerns from day one because you are forced to consider upfront what capabilities should go into each state. Once you get your state content right, your design then more accurately models the real world.
Forwarding Engine States
Figure 1 illustrates the states that are modeled using the state pattern.
Figure 1 A forwarding engine modeled with the state pattern
The engine starts up in the initial state. It can’t do a whole lot in this state apart from a transition to either the forwarding state or the exit state (just one transition is shown in Figure 1 for simplicity).
In the forwarding state, the engine can move traffic to a designated destination. From the exit state, the engine can terminate or move to either of the other two states.
State contents and transition logic can be made as complex as you want. The engine ensures that state transitions are orderly. You can also add an arbitrary number of new states just by cloning the code for one of the preceding states. Don’t worry if this all sounds very complicated. It’s not!
It’s time for a closer look at the code.