State Design Pattern in C++



The State Design Pattern is a way to let an object change how it behaves when its state changes. Instead of using lots of if-else or switch statements, you put the different behaviors into separate state classes. This makes your code cleaner and easier to understand.

In this pattern, you have a main class (called the context) that keeps track of its current state. When something happens, the context asks its current state what to do. If the state changes, the context just switches to a different state object. This way, the object can change its behavior at any time, just by changing its state.

For example, think about a simple vending machine. It can be in different states like Idle (waiting for money), HasMoney (money inserted), or Dispensing (giving out a product). Each state knows what to do when you press a button or insert money. By using the State Design Pattern, the vending machine can easily switch between states and always do the right thing, without complicated code.

State Design Pattern Illustration

Key Components of the State Design Pattern

The State Design Pattern is made up of a few important parts that work together to make your code easier to manage and understand. Here's what each part does:

  • Context − This is the main object you work with. It keeps track of what state it is in right now. When something happens, the context asks its current state what to do next. The context is also in charge of switching to a different state when needed.
  • State Interface − This is like a blueprint that says what actions every state should be able to do. All the different states must follow this blueprint and provide their own versions of these actions.
  • Concrete State Classes − These are the actual states your object can be in. Each state class has its own way of handling things. For example, the "Idle" state will do something different than the "Dispensing" state when you press a button.
  • State Transition: This is how the context changes from one state to another. When something happens (like inserting money), the context will switch to a new state so it can behave differently.

C++ Implementation of the State Design Pattern

In this C++ program, we will make a simple vending machine step by step. The vending machine will work in three main states Idle (waiting for money), HasMoney (money inserted), and Dispensing (item being given to the user). Each state will handle how the machine reacts when a user does something,like inserting money or selecting an item.

Steps to Build the Vending Machine using the State Design Pattern in C++

Following are the steps we will follow to create our vending machine using the State Design Pattern −

Steps to State Design Pattern Illustration
  • First, define a State interface that includes methods for actions like inserting money, selecting an item, and dispensing it.
  • Next, create separate classes for each state Idle, HasMoney, and Dispensing. Each class will describe how the vending machine behaves in that particular state.
  • Then, make a Context class that represents the vending machine itself. This class will keep track of the current state and will pass user actions to the state object to handle them.
  • Finally, add state transitions inside the Context class so that the machine can smoothly move from one state to another for example, from Idle to HasMoney when money is inserted, or from HasMoney to Dispensing after an item is selected.

C++ Code Example of the State Design Pattern

#include <iostream>
using namespace std;

// State Interface
class State {
public:
    virtual void insertMoney() = 0;
    virtual void selectProduct() = 0;
    virtual void dispenseProduct() = 0;
    virtual ~State() {}
};

// Forward declaration
class VendingMachine;

// Concrete State: Idle
class IdleState : public State {
private:
    VendingMachine* vendingMachine;
public:
    IdleState(VendingMachine* vm) : vendingMachine(vm) {}
    void insertMoney() override;
    void selectProduct() override {
        cout << "Please insert money first." << endl;
    }
    void dispenseProduct() override {
        cout << "Please insert money and select a product first." << endl;
    }
};

// Concrete State: HasMoney
class HasMoneyState : public State {
private:
    VendingMachine* vendingMachine;
public:
    HasMoneyState(VendingMachine* vm) : vendingMachine(vm) {}
    void insertMoney() override {
        cout << "Money already inserted." << endl;
    }
    void selectProduct() override;
    void dispenseProduct() override {
        cout << "Please select a product first." << endl;
    }
};

// Concrete State: Dispensing
class DispensingState : public State {
private:
    VendingMachine* vendingMachine;
public:
    DispensingState(VendingMachine* vm) : vendingMachine(vm) {}
    void insertMoney() override {
        cout << "Please wait, dispensing product." << endl;
    }
    void selectProduct() override {
        cout << "Please wait, dispensing product." << endl;
    }
    void dispenseProduct() override;
};

// Context: Vending Machine
class VendingMachine {
private:
    State* currentState;
    IdleState idleState;
    HasMoneyState hasMoneyState;
    DispensingState dispensingState;

public:
    VendingMachine()
        : idleState(this),
          hasMoneyState(this),
          dispensingState(this)
    {
        currentState = &idleState;
    }

    void setState(State* state) {
        currentState = state;
    }

    void insertMoney()      { currentState->insertMoney(); }
    void selectProduct()    { currentState->selectProduct(); }
    void dispenseProduct()  { currentState->dispenseProduct(); }

    State* getIdleState()       { return &idleState; }
    State* getHasMoneyState()   { return &hasMoneyState; }
    State* getDispensingState() { return &dispensingState; }
};

// Implementations
void IdleState::insertMoney() {
    cout << "Money inserted." << endl;
    vendingMachine->setState(vendingMachine->getHasMoneyState());
}

void HasMoneyState::selectProduct() {
    cout << "Product selected." << endl;
    vendingMachine->setState(vendingMachine->getDispensingState());
}

void DispensingState::dispenseProduct() {
    cout << "Dispensing product." << endl;
    vendingMachine->setState(vendingMachine->getIdleState());
}

int main() {
    VendingMachine vm;

    vm.insertMoney();
    vm.selectProduct();
    vm.dispenseProduct();

    return 0;
}

Following is the output of the above code −

Money inserted.
Product selected.
Dispensing product.

Pros and Cons of State Design Pattern

Following are some advantages and disadvantages of using the State Design Pattern

The State Design Pattern helps you write cleaner and more organized code. It does this by putting state-specific behavior inside separate classes, which removes the need for long and confusing conditional statements. However, using the State Design Pattern can also increase the number of classes in your project, which might make the codebase harder to understand and maintain at first.
It improves maintainability and scalability because you can add new states easily without changing the existing code. On the other hand, the growing number of classes may complicate navigation in the project if not organized properly.
The pattern allows dynamic behavior changes at runtime, which means the object can switch between different states smoothly. But for developers who are new to design patterns, understanding and implementing the State Design Pattern can take some time and practice.
It makes your code more organized by separating the logic of each state from the main class. This creates a better separation of concerns and makes the program easier to update later. Still, it might feel complex in the beginning and could slow down development until developers get used to the structure.

Real-World Examples of the State Design Pattern

The State Design Pattern is commonly used in many everyday software applications. Here are some practical examples −

  • User Interface Components − Buttons, sliders, and menus often behave differently based on their state (for example, enabled, disabled, or hovered). This pattern helps manage how these components react in each state.
  • Game Development − In video games, characters and objects have multiple states such as idle, running, jumping, or attacking. The State Design Pattern helps switch between these states and control their actions cleanly.
  • Network Protocols − A network connection can be in different states like connected, disconnected, or reconnecting. This pattern helps handle each state's behavior separately, making network management easier.
  • Workflow Management − In workflow systems, tasks may have states like pending, in progress, or completed. The pattern helps define how each state behaves and transitions to the next one.

Conclusion

In this chapter, we explored the State Design Pattern, its key components, and how it helps in managing state-specific behavior in a clean and organized way. We also saw a practical C++ implementation of a vending machine using this pattern, along with its advantages, disadvantages, and real-world applications.

Advertisements