Reverse the Guard Condition and Get Out Early

As I wrote in Get Out, there’s little to be gained by the flow of execution remaining inside a function body when the result is already known. In fact, it’s worth refactoring larger functions into smaller ones which return as soon as the result is known.

This has nothing to do with the efficiency of the compiler or runtime, and everything to do with the efficiency of a human reading that code.

Consider this snippet:

public boolean isAwesome(Person person) {
   if (person != null) {
       if (!person.isMadeOfLego()) {
           if (!person.isLame() && !person.isUnfriendly()) {
              return person.isAlive();
           }
           return false;
       } else {
           // everything is awesome if made of lego
           return true; 
       }
   }
   return false;
}

Four return statements, lots of indentation, what’s the algorithm here?

The rule to use is reverse the condition and get out early. This means the first refactor on the above code shifts everything left by dealing with null up front:

public boolean isAwesome(Person person) {
   if (person == null) {
       return false;
   }
   if (!person.isMadeOfLego()) {
       if (!person.isLame() && !person.isUnfriendly()) {
          return person.isAlive();
       }
       return false;
   } else {
       // everything is awesome if made of lego
       return true; 
   }
}

The code is already more readable, even more so if we drop the else, which you can do because all paths of its if return something:

public boolean isAwesome(Person person) {
   if (person == null) {
       return false;
   }
   if (!person.isMadeOfLego()) {
       if (!person.isLame() && !person.isUnfriendly()) {
          return person.isAlive();
       }
       return false;
   }
   // everything is awesome if made of lego
   return true; 
}

There’s a clue that the return true is in the wrong place in the comment. Look, it relates to an if from a few lines earlier. Let’s reverse our if and get out early a second time:

public boolean isAwesome(Person person) {
   if (person == null) {
       return false;
   }

   if (person.isMadeOfLego()) {
       // everything is awesome if made of lego
       return true; 
   }

   if (!person.isLame() && !person.isUnfriendly()) {
      return person.isAlive();
   }
   return false;  
}

Are we done? It’s certainly easier to read. I would say that we’re not. The meat of this function is to determine’s someone’s awesomeness or otherwise, and the last statement is a default of false. That’s acceptable, but would it be clearer if we understood what made it false and defaulted to the main bit of logic left after that?

To do this, we’d need to reverse the if, but it’s in the form of if (!a && !b). Do you know how to invert that? You can probably work it out, but it’s a simple bit of boolean trickery called De Morgan’s law. If you imagine a statement you want to invert as:

( MAYBE-NOT-A  A   AND-OR-OR   MAYBE-NOT-B  B )

e.g.
( A &&  B)
(!A || !B)
( A &&  B)
(!A ||  B)
....etc

To reverse the logic, you turn OR into AND and toggle the presence/absence of NOT. E.g.

e.g.
( A &&  B) == reverses as ==> (!A || !B)
(!A || !B) == reverses as ==> ( A &&  B)
( A ||  B) == reverses as ==> (!A && !B)
...etc

Going back to our example, reversing the more complex if gives:

public boolean isAwesome(Person person) {
   if (person == null) {
       return false;
   }

   if (person.isMadeOfLego()) {
       // everything is awesome if made of lego
       return true; 
   }

   if (person.isLame() || person.isUnfriendly()) {
      return false;
   }

   return person.isAlive();
}

The last move seems to work for me in this instance, but it may be preferable not to do it in other situations. It depends on the way you want your reader to understand your algorithm. Being able to invert a bit of logic with De Morgan’s law, and being able to restructure your code without nested logic, and the reader needing to keep options in their head is the main objective.

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 )

Google photo

You are commenting using your Google 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 )

Connecting to %s