Monday, April 24, 2006

Beware Bags of Bools

OSNews is running an article about using Finite State Machines in your C++ code. The write-ups are only marginally interesting in my opinion. (I prefer more efficient implementation strategies, although that does equate to more implementation effort.) It did remind me about one of my pet peeves and a lesson I learned while having to maintain other people's code:

Beware bags of Bools! (Do not storing state as a collection boolean variables.

If you have ever had to maintain non-trivial code that uses a proliferation of flags to indicate the current state, you know what I mean. Does flag foo matter when flag bar is set? Or you fix a bug only to find that your fix breaks some esoteric case where the code can't handle that particular combination of flags. Worse, each flag you add doubles the total 'state-space', i.e. the set of potential states that the code has to handle. Just 5 flags equates to 32 potential combinations. Are you testing every code path with all 32 potential combinations? I didn't think so.

The solution? Store the majority of you state as an instance of an enum. All state changes should be of the form:

switch (currentState)
{
case State.PreInit:
init(); break;
case State.Closed:
throw new error("already closed");
default:
break;
}
currentState = State.Foo;

Now when you look back at the code in 2 weeks (or 2 years), you know what states you though needed some sort of setup, what states are invalid, etc. You can add new states and know (mostly) everywhere that you need to add new cases. Most importantly, when writing/debugging the code initially, you can see what states are handled and what states are not. When I would code-review code like the above, I just need a list of all State values, and I can walk validate that the code isn't missing some scenario. Our brains can easily handle walking an enum of 32 items. We can not manage the same when there were 5 boolean flags. Just try it. I dare ya!

Being an XML guru (of sorts), I tend to write a disproportionate number of XML-ish parsers and writers. State management like the above has become invaluable to me. Have you ever tried to implement the full System.Xml.XmlReader API, with it's funky MoveToAttribute() and MoveToAttributeValue() modal behaviour? I've implemented it multiple times. My first attempt used flags. My 'cleanup' shifted the code to use explicit states. I found and fixed numerous bugs, just in the cleanup! Writing the code with explicit states made the final code dramatically clearer. Often when debugging, the resolution was simply that I had forgotten to handle one state. Add a switch and away you go. Even better, because the states are explicit, I know I'm not impacting some other scenario.

Do a favor for the future maintainer of your code. Don't store your state as a grab-bag of bools.

0 Comments:

Post a Comment

<< Home