The State Pattern

This is the last article for the foreseeable future about object oriented patterns. A lot of of the information learned and gathered is from a book called Head First Design Patterns, which I highly recommend. It breaks down each pattern is digestible amounts, and a few of the examples I used are from the book.

Since you are reading this article on the internet, I assume you’ve ordered an item off of something like Amazon or a similar e-commerce website. Lets think about the different states of an order.

First off state in this context is the status of an order. To think about the different states, we would think about the status of an order from the moment you click the checkout button, until that order gets delivered to your door.

Here are the states related for an order. Unpaid, paid, shipped, received.

Different functions would then be associated for changing the state of an order.

A user submitting credit card info, and the website verifying that info would move the state from unpaid to paid. Packaging the order and shipping it from the warehouse, would move the state of the order from paid to shipped. UPS or Fedex sending the store confirmation of delivery would move the state of an order to received.

We are going to use the state pattern to ensure that each state is responsible for how they handle each of these methods that can be called on an order. For instance we don’t want to ship an order out if it is not yet paid, or for UPS to deliver the order if the customer hasn’t paid for it.

First we want to define a state interface, and then each state will implement this interface.

The unpaid state would implement each of these functions, but the only time state is going to be changed is when we can verify that customers credit card info. If it is valid then we will change the state from unpaid to paid.

Each state will implement these methods differently, but only one method will cause the state to change. Calling ship will change the state from paid to shipped, and calling order delivered will change the state from shipped to delivered.

Now lets take a look at the order class.

On line 7 we can see the default state of an order is unpaid, which makes sense. We then have getter methods for each state of an order, unpaid, paid, shipped and delivered. Then calling the verify credit card, ship, and order delivered methods will then call those methods on whatever the current state is.

The downsides to doing this is we end up creating a lot more classes, we are creating one for every state of an order. Also you could argue we are breaking the Interface Segregation Principle, we are implementing each function from the state interface, but simply printing that a method is not allowed seems like an empty implementation.

The upside is it makes our code more extensible, if we want to add a new state to an order, we can define how it handles the different methods accordingly. Also this code is more object oriented focused than the alternative.

The alternative is for each of our methods that involved changing state we would have a big if else conditional checking the state, if it is a certain state than it would do one thing, or another, or another, and this is more procedural and less object oriented.

Remember in all these patterns we have benefits and costs. A lot of times the benefit is more readability, extensibility, and DRYer code, with the cost often being more classes, and therefore one or two more balls we have to juggle mentally to understand how objects interact.