State Pattern

“Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.”

Design Patterns: Elements of Reusable Object-Oriented Software

In the series of the Gang of Four design patterns, I wrote about the Command, Chain of Responsibility, Iterator, Mediator, Interpreter, Memento, and Observer patterns. These are part of the Behavioral pattern family that address responsibilities of objects in an application and how they communicate between them at runtime. In this post, I will discuss about the State Pattern – an important pattern that allows an object to change its behavior when it’s internal state changes. The State Pattern implements the Open Closed and Single Responsibility principles that are part of the SOLID design principles.

State Pattern: Introduction

In enterprise applications, some domain objects have a concept of state. Behavior of domain object (how it responds to business methods) depends on its state, and business methods may change the state forcing the object to behave differently after being invoked. The State Pattern encapsulates state of such domain object into separate classes and at runtime delegates to the object representing the current state – through what we know as polymorphism.

The best way to understand the State pattern is by an example. Let’s examine the State pattern through a candy vending machine example. The requirement of the vending machine is to roll out a candy whenever a user inserts a coin into the machine and presses a button. From this requirement, we can see that the machine will have four states: No Coin, Contains Coin, Dispense, and No Candy. These states represent different behavior of the machine. State transitions will move the machine from one state to another. As an example, if the current state of the machine is No Coin, and then a user enters a coin, a state transition will move the machine to the Contains Coin state. Here is how the state diagram of the candy vending machine looks like.

State Patterh - State Diagram of the Candy Vending Machine
State Diagram

In the figure above, the red circles represents the states of the machine and the arrows the state transitions.

As a programmer, if you are asked to write the code for the candy vending machine, you probably think it should be simple. And yes, it is a simple task. We could have a single CandyVendingMachine class. The class can have instance variables to represent the states and methods to represent the actions (insert coin, press button, and dispense). In each method we put in some conditional statements to define how the machine will behave in the different states. This is how our class will look like.

We have ended up with a monolithic class with three action methods, each with a bunch of repeated conditional statements to handle different types of behavior of the machine – A clear violation of the Single Responsibility principle. Now, imagine that a change request comes in to add an action that allows a user to withdraw a coin. We need to open up the class and add a new action method again with a branch of conditional statements. What if a change request for the existing press button action comes in? Say, the machine should roll out two candies when a user presses the button again exactly after a random number of seconds (say after 3 seconds or 5 seconds) that the system decides for each transaction. Again, a whole lot of changes.

Our single monolithic class is very brittle. As business requirements change, the class evolves. Programmers are tempted to add one more if statement for each change in the business requirements. This is what leads to spaghetti code. Left unchecked in a large enterprise system, you get a Big Ball of Mud. This is not a sustainable design pattern, nor is it a path to quality software.

It’s clear our class is not adhering to the Open Close principle. What we need is a system resilient to such changes and this is where the State pattern comes in. By applying the state pattern, we will put each branch of the conditional statements in a separate class. This will enable treating the vending machine states as objects that can vary independently from its peer objects. What we are essentially doing is following the principle of “Encapsulate what varies”.

Participants of the State Pattern

Before we apply the State pattern to our candy vending machine example, we will look at the participants of the pattern. We will define a CandyVendingMachineState class that will contain methods for every operations of the machine. For each state of the machine we will create a separate class. These classes are responsible for defining how the machine should behave in the state that the class represents. As we have four states, we will create four classes and name them NoCoinState, ContainsCoinState, DispensedState, and NoCandyState. We will have the CandyVendingMachine class, but now it will represent information that is mostly fixed. Our refactored CandyVendingMachine class won’t have any conditional statements. Instead it will define interface methods for clients that will delegate to the object representing the current state. We can now summarize the participants of our vending machine example as:

  • Context (CandyVendingMachine): Provides and interface to client to perform some action and delegates state specific requests to the ConcreteState subclass that defines the current state.
  • State (CandyVendingMachineState): Is an interface that encapsulates the behavior associated with a particular state of the Context.
  • ConcreteState subclasses (NoCoinState, ContainsCoinState, DispensedState, and NoCandyState): Concrete classes that implements a behavior associated with a state of the Context.

Applying the State Pattern

We will now apply the State pattern to the candy vending machine example. Let’s start with the State interface.

CandyVendingMachineState.java

The CandyVendingMachineState interface that we wrote above declares three method representing the operation that can be performed on the machine.

We will next write the ConcreteState subclasses. Let’s start with the NoCoinState class.

NoCoinState.java

We wrote the NoCoinState class above to implement the CandyVendingMachineState interface. In this class, we maintained a reference to CandyVendingMachine that is initialized through the constructor. We implemented the interface methods to define how the machine should behave when the corresponding operations are carried out and when there is no coin present in the machine. In the insertCoin() method, we changed the current state (No Coin) of the machine to the Contains Coin state represented by the ContainsCoinState class. In the pressButton() and dispense() methods, we simply printed out appropriate messages as obviously our machine will not roll out any candies if no coin is inserted.

Let’s write the other ConcreteState subclasses.

ContainsCoinState.java

DispensedState.java

NoCandyState.java

As you can observe, the remaining ConcreteState subclasses are structurally same. The difference is how we implemented the behaviors when the machine is in a particular state.

We will next write the Context – the CandyVendingMachine class.

CandyVendingMachine.java

In the CandyVendingMachine class that we wrote above, we defined instance variables for all the four states. In addition we have an instance variable state that will hold the current state of the machine and count that represents the number of candies available in the machine. In the constructor we initialized all the instance variables. We defined two internal methods refillCandy() and ejectCandy() but observe the more important interface methods (that clients will invoke) insertCoin() and pressButton(). In the insertCoin() method, we delegated the call to the insertCoin() method of the current state object. In the pressButton() method we delegated the call first to the pressButton() method and then to the dispense() method of the current state object. The remaining code are the getter and setter methods of the instance variables.

As you can see, in the code that we have written so far, all the ConcreteState subclasses are closed for modifications and our CandyVendingMachine class is open for extension – we can add in a new state. Yes, our example is now adhering to the Open Close principle. Also, we have separate classes for each state where each class is only responsible for how the machine behaves in that state – we are following the Single Responsibility principle.

Let’s now write some test code and see how our candy vending machine operates in different states.

CandyVendingMachineTest.java

In the test class above, from Line 13 – Line 19, we tested how the machine behaves when a user inserts a coin and presses the button. From Line 21 – Line 25, we tested how the machine behaves when a user presses the button without inserting a coin. From Line 27 – Line 38, we tested how the machine behaves when it is out of candies and a user inserts a coin and presses the button.
The output of the test is this.

Summary

When you apply the State Pattern, you will always have more classes in your application. Novice programmers may believe this is a bad thing. More classes, more files, more objects to maintain. They may worry this is an extra burden on the computer’s resources. It’s not. The JVM is quite optimized for handling 10’s of thousands of classes.

But avoiding the State Pattern is a sure path to unmaintainable code. Look at the above example with the nested logic. This will become a maintenance nightmare. More time is spent reading code than writing it. Think about when you as a programmer need to modify this code 6 months later. You’ll need to carefully examine it to figure out what it does. And then you’ll need to figure out how to make your changes without breaking other things.

With the State Pattern, your program logic becomes more specific, more encapsulated. Your unit tests are more ‘unity’, which is what you want. When you need lots of unit tests to test the behavior of a single class, your class under test may have a problem. The State Pattern is a classic GoF design pattern because it has stood the test of time. Developers in many different Object Oriented languages have used the State Pattern to produce quality software. Software that is easily maintained as the business requirements grow and change.