Boolean parameters are tempting but make life difficult. Matthew Wilson advises us to avoid them (almost) all the time.
|
TL;DR
Writing fns with bool params quick&easy; costs maintainers of cli-code far more effort than U save
Bite
As I’ve discussed previously on a number of occasions ([ XSTLv1 , QM#1 , QM#2 ]) and as is evident from deductive reasoning alone, combinations of characteristics of software quality are often in conflict: expressiveness vs efficiency ; efficiency vs portability ; and so forth. This is not always the case, however, and usually the sub-divisions of the composite characteristic discoverability & transparency (see sidebar) collaborate well and, further, tend to also to work well with others, especially modularity , but also (depending on how good is the design of the given component) with expressiveness , flexibility , portability , correctness / robustness / reliability , and, even, efficiency .
However, sometimes these very collegial subdivisions work against each other. In my experience, the most common example of this is when it comes to using Boolean parameters.
Consider a programmer tasked with writing an API for the manipulation of fonts, such that one is able to create a font from a base family along with selecting emboldenment, italicisation, underlining, and superscripting. The API might look like the following:
Font CreateFont(string family, bool bold, bool italicised, bool underlined, bool superscripted);
From the perspective of discoverability of the API, this is great: As a user I specify the family, then each of my (Boolean) choices as to whether it is emboldened, italicised, underlined, and superscripted. What could be clearer?
Similarly, the transparency of the implementation of this is likely to be high, since the name of each of the parameters clearly indicates its intent.
Definition of Discoverability & Transparency |
Discoverability is how easy it is to understand a component in order to be able to use it. Transparency is how easy it is to understand a component in order to be able to change it. |
However, the problem comes in the transparency of the client code . What does any of us know of the majority of the intended semantics of the following statement:
Font f = CreateFont("Courier", true, false, false, false);
This violates one of the most important of the Principles of UNIX Programming (PoUP) [ AoUP , XSTLv1 ]:
Principle of Transparency : Design for visibility to make inspection and debugging easier.
If I want to understand the statement in the client code, I have to look at the API. The use of booleans has made me do work I should not need to do in an ideal world. Thus, there’s another PoUP violation:
Principle of Economy : Programmer time is expensive; conserve it in preference to machine time.
(NOTE: I’m not even going to touch on the nightmare of what happens if the API designer decides to reorder some of the parameters …)
So what’s the humble programmer to do? The answer is simple to state and, at least for those languages that support enumeration(-like) types, straightforward to implement: make each parameter be of a different type.
API:
Font CreateFont(string family, Emboldenment bold, Italicisation italicised, Underlining underlined, Superscripting superscripted);
Client-code:
Font f = CreateFont("Courier", Emboldenment.Bold, Italicisation.None, Underlining.None, Superscripting.None);
For certain, this is somewhat more effort to write (although if you have an Intellisense-like editor it may well be less effort and faster). But without a doubt your maintenance efforts will be massively less.
Note that this phenomenon doesn’t even have to involve multiple parameters. One of my longest standing mistakes in this respect was in the constructors of reference-counting smart pointers, as seen in listing 1.
// namespace stlsoft template <typename T> class ref_ptr { . . . public: // Construction ref_ptr( T* pt , bool addRef ); . . . |
Listing 1 |
To be sure, when looking at uses of this in client code you don’t have the ambiguity of multiple parameters, but you still have to know what the second parameter of
stlsoft::ref_ptr
’s constructor does. And it’s harder to grep your code for reference copying vs owning in a codebase.
So, the advice is: Avoid Boolean parameters (almost) all the time.
References
[AoUP] The Art of UNIX Programming , Eric S. Raymond, Addison-Wesley, 2003
[QM#1] Quality Matters: Introductions and Nomenclature, Matthew Wilson, Overload 92, August 2009
[QM#2] Quality Matters: Correctness, Robustness, and Reliability, Matthew Wilson, Overload 93, October 2009
[XSTLv1] Extended STL, volume 1: Collections and Iterators , Matthew Wilson, Addison-Wesley, 2007