Friday, 3 July 2015

Campaign against if-else

Hello there, writing again after long time. Going by the rate of increase in intervals between my blog posts, I won’t be surprised if my blog ends up having higher frequency than Halley’s Comet, and may be scientists will develop some complex space-time equations to find out when the next blog will be published (in scale of light years). Constant used in these equations will be called Folderol's constant. Anyway, let’s keep these orbital calculations aside for now, we will surely discuss it sometime in future (before Hyakutake Comet comes back).

Today’s blog is not about any specific technology or framework. It explains some traits which, apart from being useful in your day to day java programming, will also make the code cleaner, more readable and easy to maintain. As the blog title says, it explains some ways to skilfully avoid ‘if..else’ statements in your program.  Please note that I am taking nothing away from usefulness/importance of this control structure (remember; if-else is a control structure and not a loop! ('Wee need to change this if loop', how many times did you say this in discussions?)) and neither do I hate if-else, however, I believe excessive use of these makes your code look ugly and complex. And, by excessive use, I mean more than 2 consecutive/nested if statements. Have a look at the below code:

if(a > 0){
      if(b > 0){
            if(b > 1){
                  System.out.println("Have you ever thought about re-evaluating your life?");
            }
      }else if(c > 0){
           
      }
}

The code is not only amateur but also difficult to debug/maintain. Imagine having to put another check in this structure as a part of change request, nightmarish, isn’t it? Below are the examples of how to deal with such situations.

1. Enhanced for loop: Null pointer exception

Here, I am assuming that everyone knows about enhanced for loop (more info here). Along with all the flexibilities it provides, there is also a catch associated with it. If the array/collection being iterated is null then, it throws a big fat NullPointerException. There are instances where the array/collection being supplied is retrieved at runtime and you don’t know whether it would be null. So, a simple way to prevent that is:

List<String> names = getNames();
if(null != names){
for(String name : names){
     
}
}

As much as I love using enhanced for loop, I don’t like an annoying if statement on top of every loop. I know we can’t get away with that, so, to prevent that, I use the following approach:

/**
 * Returns empty list if supplied list is null
**/
private static <T> List<T> safeList(List<T> input) {
    if (input == null) {
        return Collections.emptyList();
    } else {
        return input;
    }
}

private void myMethod(){
      List<String> names = null;
      for(String name : safeList(names)){
           
      }
}

Voila! We have just changed the place of the if statement. But the code now looks clean and if-free, doesn’t it? Below is an example of how to use it in case of an array.


/**
 * Returns empty array if supplied array is null
**/
private <T> T[] safeArray(T[] input, Class clazz) {
    if (input == null) {
        return (T[])Array.newInstance(clazz, 0);
    } else {
        return input;
    }
}
     
private void myMethod(){
      String[] names = null;
      for(String name : safeArray(names, String.class)){
           
      }
}

As arrays need type infotmation, the methods takes class type as an argument.

2. Multiple checks of instanceof operator

Consider a scenario where you have a reference of super class and you are getting an object of one of its sub classes a runtime. Now, you want to perform specific action depending upon the type of sub class. Let’s assume that we have a reference of Number class and it gets assigned to an object of any class which ‘Is a’ Number. Depending on the object, we want to perform specific action (Before you all jump on me with method overriding, let me clarify that specific action here means calling specific method of impl class). This is how your code will look like:


private void myMethod(){
      Number number = getNumber();
      if(number instanceof Integer){
         //cast it and do some action 
      }else if(number instanceof Double){
         //cast it and do some action  
      }else if(number instanceof BigDecimal){
          //cast it and do some action 
      }
      //And so on..

}

Imagine having more impl classes here. We will end up with a long sequence of if-else blocks (which will of course, boil my blood). We can’t even use switch as it doesn’t go hand in hand with instanceof operator. What to do here then? Below is the answer:

private enum NumberType {
    Integer,
    Double,
    BigDecimal,
    Unknown;
}

/**
 * Returns number type based on object passed
**/
private NumberType getNumberType(Number number) {
      NumberType type = null;
    try {
        type = NumberType.valueOf(number.getClass().getSimpleName());
    } catch (IllegalArgumentException ex) {
        type = NumberType.Unknown;
    }
    return type;
}

private void myMethod(){
      Number number = null;
      switch (getNumberType(number)){
      case Integer:
            //cast it and do some action
            break;
      case Double:
            //cast it and do some action
            break;
      }
}

Let me explain this in brief:

We know switch works with enums. So, we are converting the instance into enum, depending upon class type. This needs an enum to be created with all possible class types (e.g values being class names), NumberType serves the purpose here. Now, we need a method which would convert the object into enum type (using class name), getNumberType() helps us in that. And finally, a switch case to distinguish between different types and decide which action to perform.
So, we have successfully converted a lengthy if-else structure into more readable switch case. It’s good, isn’t it? Also, if we want to support more types in future then, all we need to do is to add a new enum type and a case in switch.

3. Case insensitive string comparison

Well, I am not going to provide an example for this. Java 7 included support for strings in switch case. However, there are cases where we need case insensitive string comparison, which tempts us to fall back on old school if else blocks with String’s equalsIgnoreCase() metod. Wait! There is another way to stick to the switch structure. How about providing a lower case version of string to switch (e.g. switch(name.toLowerCase()) and creating all cases with lower case strings? It kinds of solves the purpose, doesn’t it? This is self explanatory and hence no example.

4. Multiple Arguments in a method

This is not related to if-else structure but as we are already discussing code and design level stuff, I thought of including it. Have you ever come across a scenario where you need to modify the method (i.e. to add an argument) and it already contains say 6 arguments? What will you do in this case? Add 7th argument? Not if you are a true programmer.

There  are two questions which arise here. (i) When to decide whether a method has too many parameters? (ii) What is the optimal number of parameters a method should have?
Answer to (i) is, when you start googling about alternative ways to reduce method parameters, it probably has too many. And (ii) many programming experts claim that 3 to 4 is an optimal number.

Coming back to our original scenario, what to do if you want to add a parameter to a method which already has plenty? A pattern called ‘Builder Design Pattern’ comes to the rescue here. It mainly consists of creating a wrapper class containing all the parameters, creating an object of that class and supplying that object to a method (variation of DTO pattern). If we go in details then we may end up with another blog, so I will share a link of the pattern and example. Click here for more details.

5. Alternatives of nested if-else statements

At last, we will discuss the solution to the problem described at first (quite a stacky way to discuss). One alternative here is to divide the code into multiple methods (like isValid(), isGreater(), isBetween()) and call those methods, e.g. isBetween() may take a number and a range and return true of number falls within that range. If all the conditions are on an object of same class then another alternative is to write criteria/search method (e.g. search(), filter() etc) and pass the object to check if it meets certain criteria.
Another approach is to use Validator Pattern to validate object/parameters. Have a look at the example here. If you want to go a level deeper, then use can implement Strategy Pattern as well. Any of these approaches will make your code a lot cleaner.

So, this is your lot for today. Hope it will help you at some point in your programming life. I will try to be back soon, before the arrival of Halley’s Comet at least.

~ Au revoir