Learn the State Design Pattern
This is the 20th post in a series on design patterns.
State is a behavioral design pattern that lets an object alter its behavior when its internal state changes. It appears as if the object changed its class.
Imagine that you have a class called Order for ordering Pizzas. The order can be in one of four states: Confirmed, Baked, Dispatched, or Delivered. The next method of the order works a little bit differently in each state. For instance, in Confirmed, it moves the order state to Baked. In Baked, it moves to Dispatched, and in Dispatched, it moves to Delivered.
State machines are usually implemented with a lot of conditional operators that select the appropriate behavior based on the object’s current state.
Once we add more and more states and state-dependent behaviors to the Order class, the biggest weakness of a state machine based on conditionals becomes apparent.
State Design Pattern
The State design pattern suggests creating new classes for all the possible states of an object and extracting all state-specific behaviors into these classes.
Instead of implementing all behaviors on its own, the original object called context stores a reference to one of the state objects that represents its current state and delegated all state-related work to that object. Replace the active state object with another object representing the new state to transition the context. This is only possible if all state classes follow the same interface and the context interacts with these objects through the interface.
While it looks similar to the Strategy pattern, there is one key difference. State patterns involve states that know about each other and initiate transitions from one state to another, whereas strategies are rarely aware of each other.
UML Class Diagram
Not familiar with UML Class Diagram? I have written a detailed post on the UML Class diagram.
Implementation steps
- Decide which class will act as the context. It could be an existing class that contains state-dependent code or a new class if the state-specific code is spread across multiple classes.
- Define the state interface. It may mirror all the methods declared in the context, but only those containing state-specific behavior should be aimed at.
- Create a class for every state that derives from the state interface. Then, go through the methods of the context and copy all the code related to that state into your class.
- Add a reference field of the state interface type and a public setter that allows it to be overridden in the context class.
- Replace the empty state conditionals in the method of the context with calls to the corresponding methods of the state object.
- Create an instance of one of the state classes and pass it to the context to switch its state.
Source Code Implementation
The Order (Context) class stores a reference to one of the concrete state objects and delegates all state-specific operations to it. The context communicates with the state object using the state interface. There is a setter in the context for passing a new state object.
State-specific methods are defined in the OrderState (State) interface.
ConfirmedState, BakedState, DispatchedState & DeliveredState each implement their own state-specific methods. Concrete State objects may store a reference to the context object. This reference allows the state to fetch any required information from the context object, as well as initiate state transitions.
Likewise, contexts and concrete states can both set the next state of the context and perform the actual state transition by replacing the state object linked to the context.
// Output Order Confirmed! Order Baked! Order Dispatched! Order Delivered Yeh!
When To Apply State Design Pattern
- Use the State design pattern when you have an object that behaves differently depending on its current state, the number of states is large and the state-specific code changes frequently.
- The pattern should be used whenever you have a class polluted with massive conditionals that alter the behavior of the class according to the values of its fields.
- If you have a lot of duplicate code across similar states and transitions of a condition-based state machine, use the State design pattern.
Pros of State Design Pattern
- Separate the code related to different states into separate classes.
- Introduce new states without changing existing state classes.
- Simplify the context code by eliminating bulky state machine conditionals.