The State Design Pattern Explained
The State Design Pattern is a behavioral design pattern that allows an object to change its behavior when its internal state changes. It was introduced in the famous “Gang of Four” book titled “Design Patterns: Elements of Reusable Object-Oriented Software“. By the end of this article, you will have a clear understanding of this pattern and its trade-off. If you wish, you can also find the same content in video format on my YouTube channel.
Definition and Motivation
The state pattern allows an object to alter its behavior when its internal state changes.
Imagine we are modeling an order with different states. An order can be cancelled when it’s in Draft or Confirmed state. Once Shipped, it can either be successfully Delivered or marked as a Lost and eventually Refunded.
If we modeled all the possible states as an enumeration in the Order class, we’d be forced to use conditional statements to alter the behavior of the class depending on its state. This is fine if we are modeling a few states with simple business logic, but it becomes difficult to manage if we have many states with complex rules.
The State pattern models each state as a separate class. These classes implement a common interface that defines all possible operations in the Context that depend on its state. At any point in time, we can have only one state.
In our case, the context would be the Order
class where the state is stored as a property. When the Order
receives a request that requires state-specific logic, it delegates it to a concrete state object by invoking the relevant method as declared in the interface.
Let’s have a look at the actual code.
In our scenario, the OrderState
is a simple interface with default methods implementations.
The Order
class is the Context object. As per pattern, it forwards requests to the concrete state objects. We set DRAFT
as the initial state.
If we have a quick look at the DraftOrder
, we can see that it supports only the confirm and cancel operations as per requirements. There’s another important thing to note. The context passes itself as an argument. This is very important because it allows us to access the Order and modify its properties. In this case, we are taking control of the state transitions in the state object. This is a very common approach even though the pattern is not prescriptive on how we modify the context’s state. It’s also possible to define transitions in the context object itself.
State Design Pattern Advantages
So. What are the advantages of this pattern?
The main advantage is that we can partition state-specific logic in different classes. In simpler words, we can break a large class into smaller ones which are more focused. These classes are typically easier to read and test. It’s way easier to understand what happens in a particular state.
Another important advantage is that the state transitions are explicit and modeled according to a precise state diagram. The state is represented by a single property which is updated atomically. The context can have only one state.
In contrast, a conditional statement may depend on multiple variables which can be updated independently. This is way more fragile. The enumeration variable alone is not enough to establish whether a particular code branch is activated or not.
The last advantage, according to the Gang of Four, is that we can have concrete state objects with behavior and no data. The data is stored and shared by the Context. This is effectively an implementation of the Flyweight pattern which we’ll cover in another video.
I want to highlight that it’s also fine to store variables in concrete state objects. It makes sense if we have variables that are meaningful only in a particular state.
Final Thoughts
So, what do you think? Is this a pattern we should use when coding our applications?
You know my opinion: it depends!
If our logic is lean and state transitions are linear, I wouldn’t bother with it. In this case, we can easily fit this behavior in a single class with few lines of code, which is better than having to jump over multiple classes with little to no behavior. If you browse the repository, I committed a version of the Order
class that uses if statements which is kind of acceptable at this point. However, this is a synthetic class with no real data or logic. In a real-world scenario, this class would be way larger and possibly benefit from the state pattern.
If we know at the beginning of a project that the state-dependent logic is going to be very convoluted, then I would consider this pattern seriously. To give you a better idea I coded another scenario where we model a game where a player can move, aim, and shoot. The state diagram defines all possible transitions. Each state impacts our ability to aim and fire accurately.
Even if I kept the code minimal and easy to understand, we can already see the power of this pattern. I’ll let you experiment a bit with it! It’s all committed on GitHub.
I really enjoy reading your comments so keep going! Some of you wished to have 1 on 1 mentoring or brainstorming sessions so I setup a calendar on my website. Feel free to book a slot. Don’t forget to share the video or this article so that everyone can learn something new!