Thursday, November 22, 2012

Coding Patterns: Promote The Normal Flow

Working as a Java consultant, I have often been involved in discussions on coding style; and quite often, developers disagree on how to order the code blocks of if-else statements. The issue arises from the fact, that in a regular if-else statement with two code blocks A and B like this one
if(condition) {
    … A …
}
else {
    … B …
}
the two blocks of code, A and B, can easily be reversed by negating the condition like this
if(!condition) { // notice the '!'
    … B …
}
else {
    … A …
}
So, which is preferable? if-A-else-B or if-B-else-A? The answer I hear the most is: "Well... that just depends on the if condition." Unfortunately, that is not the correct answer – Far from it.

Identify the normal flow

In the statement above, the condition helps us distinguish between two different situation – one situation where the condition holds, and one where it does not. Quite often, the condition is relatively easy to write, e.g.
if(list.size() == 0) {…} 
so many developers just write what ever plausible condition that comes to mind, and use the condition to decide which code to put in the A and B blocks respectively. So let us say, that it is illegal for the list to be empty, the code might end up like this
if(list.size() == 0) {
    throw new AnEmptyListIsNotAllowedException();
}
else {
    // do something useful
    …
}
While this may be logically correct, the problem here is the code ordering. The rare case, where someone invoked this code with an empty list, now comes first, while the normal flow, comes last. That is wrong, for several reasons.

Think: Try-then-catch

When Java was designed back in the mid 90s, the designers chose to adopt the famed C++ exception facility where you could concentrate on the normal flow of you code, and then leave the rare, exceptional cases to their own appended code blocks, as in
try {
    // do something useful
    …
}
catch(AnException exception) {
    // handle the exception
    …
}
One of the advantages of this design was the fact, that both the code author, and any other developer who might later need to maintain the code, could now start out by reading and understanding the important part of the code: The normal flow. Once you have understood this normal flow, you know what the purpose of the whole code segment is; consequently, it gets much easier to understand the exception handling in the following catch blocks.
Think for a second, how you would be able to understand the purpose of a code segment if you where presented with several catch blocks first.

Reversing the condition

Obviously, the if-else example presented earlier is simple to fix, by just reversing the condition:
if(list.size() > 0) {
    // do something useful
    …
}
else {
    throw new AnEmptyListIsNotAllowedException();
}
If you start practicing this, you will notice that in more complex scenarios (if-else-else-else), this really is helpful in making your code readable. And in 12 months from now when you get back to the code to maintain it, understanding what is going on will be so much easier.