Skip to main content
Create your own
Lesson illustration

Mastering the State Pattern

Hello! Welcome to your next lesson on behavioral design patterns.

In our previous session, we explored the Strategy pattern, which allows you to define a family of interchangeable algorithms and select one at runtime. We saw how it helps organize code that performs the same task in different ways, like choosing a payment method or a parking strategy.

Today, we'll tackle the learning outcome: Apply the State pattern to allow an object to alter its behavior when its internal state changes.

At first glance, the State pattern's structure can look almost identical to the Strategy pattern's. However, their intent is fundamentally different. While Strategy deals with how an action is performed, State deals with what actions are possible and how the object behaves based on its current condition. This pattern is essential for modeling objects that move through a lifecycle of different states, a common scenario in system design interviews.

What is the State Pattern?

Many objects in software don't have static behavior. Their response to a method call depends on their internal state. Think of a smartphone: pressing the power button does one thing if the screen is off (turns it on) and another if the screen is on (locks it). This concept is formally known as a finite state machine.

The State pattern provides an object-oriented way to implement such state machines. Instead of using large if/else or switch statements within the object's methods to check the current state, we encapsulate the behavior of each state into its own class.

The State Pattern Explained and Implemented in Java | Behavioral Design Patterns | Geekific

To start, let's watch a short segment from the video 'The State Pattern Explained and Implemented in Java' by Geekific. It uses the smartphone example to introduce the core concept of state-dependent behavior and how it relates to the State pattern.

Watch from the beginning until timestamp 02:06. Focus on how the phone's response to button presses changes based on its state (Off, Locked, Ready) and the formal definition of the State pattern.

As the video explains, the State pattern lets an object alter its behavior when its internal state changes, making it seem as if the object has changed its class. This approach helps us adhere to the Single Responsibility Principle (SRP) by moving state-specific logic into separate classes and the Open/Closed Principle (OCP) by allowing us to add new states without modifying existing ones.

The Structure of the State Pattern

The pattern has three key participants:

  1. Context: The object whose behavior changes based on its state. It maintains a reference to an instance of a ConcreteState, which represents its current state.
  2. State: An interface or abstract class that defines the methods representing the state-dependent behavior. The Context interacts with its state object through this interface.
  3. ConcreteState: A class that implements the State interface. Each ConcreteState class provides the implementation for a particular state of the Context. Crucially, a ConcreteState can also be responsible for managing the transition of the Context to a new state.

Let's look at the structure of the pattern through a practical example: a package delivery service.

State Design Pattern in Java

The article 'State Design Pattern in Java' from Baeldung provides a clear, step-by-step implementation. We'll use its package delivery example to understand the code structure.

Read section '4. Implementation'. Pay close attention to how the Package class acts as the Context, delegating calls to its state object. Also, notice how the concrete states (OrderedState, DeliveredState) handle the transitions by calling pkg.setState().

From this example, we see the key dynamics:

  • The client code interacts only with the Package (Context) object, calling methods like nextState().
  • The Package object doesn't contain any if/else logic. It simply delegates the call to its current state object (e.g., state.next(this)).
  • The OrderedState's next() method contains the logic for transitioning to the next state: pkg.setState(new DeliveredState()). The state object itself controls the transition.

State vs. Strategy: A Critical Distinction

This is where many developers get confused, as the UML diagrams for both patterns are very similar. The key difference lies in their intent and dynamics.

Let's clarify this crucial distinction.

The State Pattern Explained and Implemented in Java | Behavioral Design Patterns | Geekific

The Geekific video we watched earlier has an excellent segment comparing the State and Strategy patterns. This will help solidify your understanding of when to use which.

Watch the section from 05:04 to 06:01. Focus on the differences regarding how behavior changes and how aware the individual classes (states vs. strategies) are of each other.

Here is a summary of the differences:

Aspect State Pattern Strategy Pattern
Intent Manages the state of an object. The object's behavior changes entirely depending on its state. Provides different algorithms for a specific task. The algorithms can be interchanged.
Behavior States encapsulate state-specific behavior. It's about what the object does. Strategies encapsulate an algorithm. It's about how the object does something.
State Transition Concrete states are aware of each other and manage the transitions from one state to the next. The transitions are often fixed. Strategies are independent and unaware of each other. The client or Context usually selects and changes the strategy.
Coupling Concrete states are often coupled to each other to facilitate transitions. Concrete strategies are completely decoupled from one another.
Test your understanding!

A text editor allows a user to write text.

  • Scenario A: The editor can format selected text as bold, italic, or monospace. The user chooses the format from a dropdown.
  • Scenario B: The editor can be in Edit Mode (where typing inserts characters) or Command Mode (where typing executes commands, like in the Vim editor).

Which scenario is better suited for the State pattern, and which for the Strategy pattern? Why?

Show answer
  • Scenario A is suited for the Strategy pattern. The core task is "formatting text." Bold, italic, and monospace are different algorithms (strategies) to accomplish this task. The user (client) selects which strategy to apply.
  • Scenario B is suited for the State pattern. The editor's fundamental behavior changes based on its mode (state). In Edit Mode, a key press results in a character being inserted. In Command Mode, the same key press might delete a line or save the file. The states (Edit Mode, Command Mode) dictate the object's entire behavior and manage transitions between themselves (e.g., pressing Esc transitions from Edit to Command Mode).

LLD Application: Designing a Vending Machine

One of the classic LLD interview problems you wanted to cover is designing a vending machine. This is a perfect use case for the State pattern, as a vending machine moves through a well-defined set of states: Idle, Has Money, Dispensing, Out of Stock, etc.

Let's design one. First, it's helpful to visualize the flow using a state diagram. This shows the possible states and the events that trigger transitions between them.

Vending Machine State Diagram
This state diagram shows the possible states of a vending machine (`Ready`, `Dispense Item`, `Dispense Change`, `Txn Cancelled`) and the events that cause transitions between them, such as collecting cash or a user cancelling the transaction.

Now, let's see how we can model this using the State pattern in Java. The video below provides a detailed walkthrough, which is highly relevant for an LLD interview setting.

🚀 Vending Machine System Design – LLD for Interviews & Projects 🧑‍💻

The video '🚀 Vending Machine System Design' from codeWithAryan directly tackles this LLD problem and explicitly uses the State pattern as the core of the solution.

Watch the following segments: Why State Pattern? (08:37 - 10:30): This explains why the State pattern is the primary choice for this problem. Core Components (17:06 - 19:28): This introduces the VendingMachineState interface and the concrete states. State Transitions (19:28 - 23:09): This is the most important part. It walks through the state diagram, explaining how the machine transitions from Idle -> HasMoney -> Selection -> Dispense based on user actions.

To complement the video, here is a clear UML class diagram and a complete code implementation for the vending machine. This shows the static structure of the classes you would design.

Class Diagram of State Design Pattern for a Vending Machine
This UML diagram shows the class structure for the vending machine design. It has a `VendingMachineContext` (the context), a `VendingMachineState` interface, and several concrete states like `ReadyState` and `OutOfStockState` that implement the interface.

Finally, let's review a clean implementation of these components in Java.

State Design Pattern

The article 'State Design Pattern' from GeeksforGeeks provides a full, well-documented Java implementation for the vending machine problem.

Review the sections from 'Example of State Design Pattern' through to 'Complete code for the above example'. You don't need to read every line in detail, but scan the code to see how the VendingMachineContext, VendingMachineState, and the concrete state classes are implemented. This reinforces what you saw in the video.

In a Spring Boot application, you could implement the concrete state classes as singleton beans. The VendingMachine (Context) could then have the current state injected, or it could fetch the required state bean from the application context when a transition occurs.

Conclusion

You've now added another powerful behavioral pattern to your LLD toolkit. The State pattern is your go-to solution when an object's behavior is dictated by its internal state.

Key Takeaways:

  • Purpose: The State pattern allows an object to change its behavior when its internal state changes, by encapsulating state-specific logic into separate classes.
  • Problem Solved: It helps you avoid complex conditional logic (if/else, switch) for managing states, leading to cleaner code that follows SOLID principles.
  • Core Components: It consists of a Context (the stateful object), a State interface, and ConcreteState classes that implement behavior and manage transitions.
  • State vs. Strategy: Remember the key difference in intent. State is about what an object is (its state), while Strategy is about how an object does something (its algorithm).
  • Interview Application: It is perfectly suited for designing stateful systems like vending machines, ATMs, elevators, and document workflows.

In our next lesson, we will cover the Template Method pattern. This pattern provides a way to define the skeleton of an algorithm in a base class, allowing subclasses to redefine certain steps without changing the algorithm's overall structure. It offers a different approach to code reuse and variability compared to the State and Strategy patterns.

Can't find a good explanation? Sign up and we'll make it for you

Sign up