July 29, 2013

Boolean Parameters

In Clean Code, Robert Martin strongly deplores boolean arguments to constructors. It’s one of the few code smells he identifies that’s not (I think) derived from Fowler’s original Refactoring. At first it seemed outlandish, but I think I’m convinced.

Though Martin doesn’t make a point of the exception, we’re not talking about objects that wrap a boolean state. Things like Alarm might naturally take a boolean:

Alarm::Alarm(bool isTriggered)

There’s not that much point in turning a boolean into an object, but you might want this for side effects. A TestAlarm just remembers if it’s on or off, but a LowMemoryAlarm could notify objects to clear their caches, and a FireAlarm could ring the bells, activate sprinklers, and summon the fire department.

We’re not talking about that.

What we’re talking about here are constructor flags that identify arguments or manage behavior. For example, Tinderbox 5 has

UnicodeString::UnicodeString(const string& s,bool isUTF8)

This is a legacy of the movement from MacRoman encoding to UTF8; we pass true to tell the constructor, “This is already Unicode” and we pass false to say, “This comes from a legacy source and it’s still using MacRoman text encoding.” The conversion can be expensive and so it’s done just in time; if we get a MacRoman string and we’re only asked for MacRoman, we never need to do the conversion.

There’s a nice, easy refactoring that gets rid of the parameter and clarifies your intent.

  1. Add a new public constructor without the boolean argument. The new constructor assumes the most common or normal value for the argument, and does exactly what the old constructor did. So now we have UnicodeString(s).
  2. Define a new class for the unusual case that inherits from your class. It needs only a constructor and a destructor. The constructor simply calls the original class constructor.
    LegacyUnicodeString::LegacyUnicodeString)const string& s>
    :UnicodeString(s,false){ }
  3. Make the old two-argument constructor protected.
  4. Update each place where the original class was constructed, using with UnicodeString or LegacyUnicodeString.

And we’re done. In this case, we can improve eighty-seven separate constructors with one simple refactoring that takes almost no time, while leaving a clear indication that those Legacy strings could bear attention when time allows.