Thursday, May 9, 2013

The State Pattern

During my apprenticeship, I've encountered ideas I learned about in college. When I say this, I do not mean to imply that I learned everything I could about each topic. In fact, my apprenticeship has taken those foundations and replaced what I knew with a much better understanding. One example of this is a state machine.

A state machine describes how something behaves given a current state and transitions to the next state. It doesn't have to be software either. You can make a state machine diagram for traffic lights, for example. The key properties are that a state machine can only be in one state at a time, there are a certain number of possible states, and there are transitions from one state to another. One area of programming that makes extensive use of state machines is game programming.

In college, I was taught to implement state machines in a simple way. It boils down to a rather large class with a function for each action the state machine can take, a variable containing the current state, and a large switch statement. In all likelihood, I've written something like the following before.

While this state machine doesn't really do anything, it works. There's a bigger issue than it not doing anything though. It's possible that there are additional things wrong with it but that case statement in take_action causes me pain and is a glaring example of an OCP violation. I'm not pleased by having to check the state every time that I want something to happen. Before this apprenticeship, I would have accepted this as the cost of writing a program.

I now know that an answer to my issue is the state pattern. This pattern encapsulates each game state in an object. Each of these state objects conform to the same interface and correspond to single game state. In this way, my state machine no longer cares about checking the current state when it takes each action because the state variable is an object. That object already knows what to do in the current situation. This allows me to alter my current program to look more like the following.

Using the state pattern, I was able to move the take_action method out into each of the state classes and eliminate it from the state machine. My state machine should still behave the same way, but it favors creating more classes to additional whens in a case statement. Now, as long as the transitions between states are correct, I can call take_action within my run method and let each state class do what it's supposed to.

While I find this to be a very interesting pattern, I'm more surprised by my reaction to it. Before this apprenticeship and exposure to the SOLID principles, I would have just accepted that my code became unmanageable  That doesn't mean that I would have been alright with it. Most likely I would have tried to fix it, given up in disgust, and told myself that it's because I don't know what I'm doing. I probably would have taken a break from programming and tried to forget it ever happened. Before, if I had read the state pattern, it would have looked like needless complexity. Instead, I see it now as a way to continue using state machines without violating SOLID principles and leading me to more maintainable code.

Things feel different now. I can recognise when I'm going down a bad path. I know that there are ways to avoid it. Unfortunately, I sometimes realize this halfway down the bad path. There's still a long way to go before I know everything I need to. While that's only a subset of all knowledge and given how long it's taken me to get where I am, it's doubtful I'll ever know everything I should. I'm okay with that though. It just means I need to keep in mind how much farther there is to go.