zondag 9 december 2012

If-statements considered harmful ?

Almost every programmer knows about the paper/letter 'Go To Statement Considered Harmful' by Dijkstra, or maybe they didn't read the article or even don't know that he was the author, but at least they are familiar with the quote.  Now I am not claiming If-statements should be banished, but I do think their use could be less excessive. Of course when programming in certain languages it tends to need more If-statements then with others. For example filtering data in Java is most of the time implemented with a loop and an If whereas functional languages use different constructs. But programming is all about creating abstractions and a lot of usages of if-statements can be leveraged to higher level concepts, even with non-functional languages like Java. With this blog post I try to show an alternative to some of the usages of If-statements that I practice in my code bases.
One typical example for if-statements is to do boundary checking.

public int sum1(final List<Integer> values){
 if(values == null){
  throw new IllegalArgumentException("sum only supports non-null collections");
 }
 int sum = 0;
 for(Integer val:values){
  sum += val;
 }
 return sum;
} 


Here we see the use of if statements to conditionally throw exceptions. These situations occur very frequently when writing programs in defensive styles, which is mostly, but not exclusively, practiced in systems that have high standards for availability.
To simplify this code, without changing its behavior I propose the following API.


/**
 * Defines the methods for the assertions
 */
public interface Asserter {

 /**
  * check for null value
  * 
  * @param object
  * @param objectName
  *            descriptive name of the object
  * @throws RuntimeException
  *             for null values
  */
 void assertNotNull(final Object object, final String objectName);

        // other methods removed for brevity
}

public class Assertions {
 
 /** useful for static import of members */
 public static final CheckedAsserter checked =  new CheckedAsserter();
 public static final IllegalArgumentAsserter argument = new IllegalArgumentAsserter();
 public static final IllegalStateAsserter state = new IllegalStateAsserter(); 
}



Which, when applied to the former example would lead to: (it uses a static-import of Assertions.argument)

public int sum2(final List<Integer> values){
 argument.assertNotNull(values, "sum only supports non-null collections");
 int sum = 0;
 for(Integer val:values){
  sum += val;
 }
 return sum;
} 


Although readability is a matter of taste, my opinion is that the latter is far easier to understand and reason about. For one, because it reads out the intend by hiding (the somewhat trivial) implementation of the check. And even though the implementation of the check is trivial, the revisited code's layout is easier on the eye, and reading it makes it even more clear what is going on. But also when calculating it's McCabe complexity it is considered less complex.
Another typical (and somewhat comparable) use is the handling of null-references to Collections. Lets assume we want a null-collection to have a sum of 0, instead of leading to an exception.

public int sum3(final List<Integer> values){
 if(values == null){
  return 0;
 }else{
  int sum = 0;
  for(Integer val:values){
   sum += val;
  }
  return sum;
 }  
}


Without this check, and with a null reference, the for loop would result in a Null-pointer exception. For this is propose the following API.

public <E> List<E> getOrEmpty(final List<E> list) {
 if (list == null) {
  return Collections.emptyList();
 } else {
  return list;
 }
}


which, when applied, results in : (by considering the null as an empty collection the code no longer has an alternative path.

public int sum3(final List<Integer> values){
 int sum = 0;
 for(Integer val:getOrEmpty(values)){
  sum += val;
 }
 return sum;
}


Now later I realised this was just an example of a more broader phenomenon, the use of a variable or a default alternative.
Hence the following API was created:

public static <T> T getOrDefault(final T val, final T _default){
 if( val != null ){
  return val;
 }else{
  return _default;
 }
}


I wouldn't say it replaces the former, since the name getOrEmpty says more clearly its intent then getOrDefault, yet it could be used in the same circumstances. I mostly use it at parsing code for now, where parsing could result in the parse-result, or a default. Another way I use it is when another API is used which does not return default values but null's in which case I use these getOrDefault calls to integrate it into my algorithms that expect a correct value.

private static int NO_ORDERS = 0;
 
public int getAmountById(final int id){
 return getOrDefault( orderDao.findAmountForOwner(id),
         NO_ORDERS) ; // return null when id has no orders
}


On the creation of the asserter API. Of course I could have used Java-asserts but my approach allows for more flexibility (choosing the appropriate exception to be thrown for the situation) also these Assertions are always-on whereas the Java-native one can be enabled/disabled with java command-line arguments. It should be noted that there are other assertion classes



  • Spring Assert
  • Commons.lang.Validate
  • Hibernate Validator 

  • This one is more flexible than 'Spring' and 'Commons' because of the exception-factory, that allows to specify what exception to throw. On the other hand Hibernate Validator will be a reference implementation for the JSR Bean Validation. Which uses annotations for declarative bean validation. It is better suited for class hierarchies but on the other hand is a bit more invasive to use. Yet it does support localization through resource bundles.
    If a lot of business rules will be implemented Hibernate Validate could assist the functionality of this class.
    This class is more useful when for pre-post condition validation