So, my impetus for writing this post is the ‘require’ method in scala. It works a bit like the failed ‘assert’ that came to Java a while ago but never really took off. The ‘require’ in scala is used to write guard conditions, similar to assertions you may make in JUnit tests – here is an example:
object Shuffler { def shuffle(deck:Deck) = { require(deck.isNotEmpty, "deck cannot be empty") // code omitted... } }
In Java I’ve seen the following:
public void shuffle(Deck deck) { // guard condition if (deck.isEmpty()) { throw new IllegalArgumentException("Cannot shuffle an empty deck"); } // code omitted... }
and also
public void shuffle(Deck deck) { // guard condition if (deck.isNotEmpty()) { // code omitted... } else { throw new IllegalArgumentException("Cannot shuffle an empty deck"); } }
This style of code can quickly create multiple nested ifs and make the code less readable. An old adage said “no more than one return point in a method” but this rule has not stood the test of time. As Kent Beck writes, this “was to prevent the confusion possible when jumping into and out of many locations in the same routine. It made good sense when applied to FORTRAN or assembly language programs written with lots of global data where even understanding which statements were executed was hard work … with small methods and mostly local data, it is needlessly conservative.”
Option 1: Roll-your-own Guard Condition
We fail fast – error if the condition we cannot handle is true. Note – now our happy path code (omitted) is not inside an if or else. We can write multiple guard conditions but can recongnise the guard conditions for what they are above the actual part of the method that affects change.
public void shuffle(Deck deck) { // guard condition if (deck.isEmpty()) { throw new IllegalArgumentException("Cannot shuffle an empty deck"); } // code omitted... }
Option 2: External Libaries
I think we can improve on this using a static method similar to the Assert class we use in JUnit, and classes to do this already exist. Our options include Validate (commons lang), Assert (Spring framework) and Preconditions (google-guava).
Here is an example using Preconditions:
import com.google.common.base.Preconditions; public void shuffle(Deck deck) { // guard condition Preconditions.checkArgument(deck.isNotEmpty(), "Cannot shuffle an empty deck"); // code omitted... }
This is much simpler and easier to read for me. I believe that, as a rule of thumb, the code doing the primary purpose of a function should generally be equal to the amount of code doing the following tasks: Logging, Auditing, Error Handling, Guard Conditions.
A good comparison of the options is available here: http://www.sw-engineering-candies.com/blog-1/comparison-of-ways-to-check-preconditions-in-java