Enums are Evil: The State Pattern

This is actually about the state pattern. but let me talk first a bit about enums.  A big thing to think about is that “if” statements and especially “switch” statements can sometimes be a code smell.  Enums, in particular, can be a sign that something is wrong with your code.

Here’s an exercise to see exactly why enums are a problem: take some code you’ve written (not somebody else’s:  other people’s code is always bad…) and identify an enum that you’ve defined.  An enum with a large number of values is particularly promising, but even tri-states can illustrate the problem.  Now search through your code and document how often you branch on the basis of the enum.  I’m betting it’s pretty common.  Now, that in itself is a problem, but here’s where it gets worse: I guarantee that those branches are all over your code.  Picture what happens if you add in another entry to your enum.  Think of how many unrelated lines of code you’d need to check.  To make it harder, imagine that the new value is very similar, but not quite identical, in behaviour to another one.  It’s time, basically, to replace your enum concept with a proper class.

There’s three cases that you need to think about when looking at an enum this way:

  • Does the enum basically represent an action?  In that case, introducing a command pattern may be the best idea.
  • Is the enum mostly used to branch an algorithm?  Here, a strategy pattern-style outsourcing of responsibility may be the best approach.
  • Is the enum a changeable property of an entity?  In that case, the state pattern is probably appropriate.

I’ll observe that most of the arguments above also apply to boolean variables:  you need to be very careful with exposing state that is used for decision making in your code.  It can be very easy to end up with a mass of untracked dependencies.  In some ways, booleans can be even worse, because they’re harder to refactor out.

Java Enums

I’ve touched on the idea that a lot of patterns address deficiencies in our programming languages, and this is a classic case.  Enums in C# and C++ are basically a fairly light syntactic wrapper around an integer.  Enums in Java are much better.  They’re classes with a restricted number of possible values.  You can even overload methods for particular instances.  This makes implementing these patterns much more natural in Java than they are in C#.  I wish C# had Java’s enums, but it seems unlikely that Anders is going to bother.  So, to implement these patterns in C#, you’re going to need to do something like Kent’s approach for representing enums as classes.

On the other hand, it’s perfectly possible to introduce the same problems in Java code as in C# code if you don’t understand what the problem actually is.  In particular, liberally using the enum literals will drag you down the same road, no matter how good your intentions. 

The State Pattern

Quite often you have objects with a status or state pattern, usually representing a stage in a workflow process.  The state pattern doesn’t necessarily only apply to these cases, but it’s quite a common use case.  More generally, if a property of an object has significant behavioural implications, it can be a candidate for the state pattern.  To emphasize this, let’s take an example which isn’t a workflow state:

Let’s say we have a system for tracking changes to your live servers.  You’ve got three basic release types:

  • Routine maintenance
  • Ordinary, planned releases
  • Emergency work

Now, any given release might actually change which process it uses at any given time, complicating matters.  Consider if you want to know if a release is fully approved.  So, you represent the release type with a IReleaseType interface and add an IsFullyApproved method.  Here you’ve got a choice as to whether your release type object should take its parent object as state.  If it does, the interface is simpler since it doesn’t keep having to ask for the parent object.  On the other hand, it’s no longer got useful enum behaviours such as there only being one instance representing “Routine Maintenance”.  You’re not violating best practice either way.

You should definitely take a look at Davy Brion’s implementation of the circuit breaker pattern, which uses a state pattern.  (Incidentally, if you’re having trouble following the code, bear in mind that a circuit breaker prevents activity when it’s open.  When it’s closed, it passes electricity through.)

Enums as Classes

Although the State pattern is a useful and important pattern, it’s part of a much larger approach: getting rid of enums and replacing them with classes.  We’ve seen that Command and Strategy can also be used to address similar concerns.  Don’t get too hung up on whether it’s a State, Strategy or Command: replacing a rather fragile enum with a proper object instance is the order of business.  Once you’ve made the decision, a lot of standard object design principles will come into play, making the design more obvious.  As a rule of thumb, try to avoid referring to an exact enum:  e.g. “State.Open” unless you’re actually passing something to a constructor or writing a test.  The rest of the time, it should be the methods and properties of the “state” that are being used.

Some more things you need to deal with when using this pattern: what you do about persistence.  Basically, is your enum-like object an entity or just a value?  Both have their advantages.  if you want to go with a value, you’ll need to deal with persistence concerns: implementing IUserType for NHibernate and implementing a model binder in ASP.NET MVC.

Published by

Julian Birch

Full time dad, does a bit of coding on the side.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s