How to deal with nested conditionals (Part 2)

Reading Time: 2 minutes

In my previous post, I mentioned about Decomposing Conditional and Consolidate Conditional Expression for nested conditionals. The problem was not only the nested conditions but also duplicated code fragments and lots of null object controls.  In this post, we will cover Consolidate Duplicate Conditional Fragments and Testing Interface to tackle the above mentioned problems.

Consolidate Duplicate Conditional Fragments

Martin Fowler proposed that “if the same fragment of code is in all branches of a conditional expression, move it outside of the expression” in his book. In this case, only else branches have duplicate code fragment but we can use Consolidate Duplicate Conditional Fragments refactoring for our problem as well. Consider the following code:

response = new RetrieveMarketInquiryCustomerResponse();
if(condition1){
   if(condition2){
      if(condition3){
       ...
      } else {
        log.debug("retrieveCitizenAddResponse==null");
        marketInqCustomerVo.setAddress(" ");
        marketInqCustomerVo.setCity("CT200703022025160219");
        marketInqCustomerVo.setDistrict("UNKNOWN_DISTRICT__ID");
        response.setMarketInqCusVo(marketInqCustomerVo);
      }
   } else {
     log.debug("retrieveCitizenAddResponse==null");
     marketInqCustomerVo.setAddress(" ");
     marketInqCustomerVo.setCity("CT200703022025160219");
     marketInqCustomerVo.setDistrict("UNKNOWN_DISTRICT__ID");
     response.setMarketInqCusVo(marketInqCustomerVo);
  } else {
       log.debug("retrieveCitizenAddResponse==null");
       marketInqCustomerVo.setAddress(" ");
       marketInqCustomerVo.setCity("CT200703022025160219");
       marketInqCustomerVo.setDistrict("UNKNOWN_DISTRICT__ID");
       response.setMarketInqCusVo(marketInqCustomerVo);
 }

Step 1: Since the same code occurs in each else branch, we can move it to the outer else such that

if(condition1){
   if(condition2){
      if(condition3){
       ...
      }
} else {
     log.debug("retrieveCitizenAddResponse==null");
     marketInqCustomerVo.setAddress(" ");
     marketInqCustomerVo.setCity("CT200703022025160219");
     marketInqCustomerVo.setDistrict("UNKNOWN_DISTRICT__ID");
     response.setMarketInqCusVo(marketInqCustomerVo);
}

Step 2: Applying Consolidate Conditional Expression, in other words shrinking nested ugly if statements, results in:

if(condition1 && condition2 && condition3){
 ...
) else {
 log.debug("retrieveCitizenAddResponse==null");
 marketInqCustomerVo.setAddress(" ");
 marketInqCustomerVo.setCity("CT200703022025160219");
 marketInqCustomerVo.setDistrict("UNKNOWN_DISTRICT__ID");
 response.setMarketInqCusVo(marketInqCustomerVo);
}

Step 3: Then one more refined version with Decomposing Conditional and extracting else part into separate function.

if(isValid()){
 ...
) else {
  setResponse();
}

private boolean isValid(){
  return (condition1 && condition2 && condition3);
}

private void setResponse(){ 
  log.debug("retrieveCitizenAddResponse==null");
  marketInqCustomerVo.setAddress(" ");
  marketInqCustomerVo.setCity("CT200703022025160219");
  marketInqCustomerVo.setDistrict("UNKNOWN_DISTRICT__ID");
  response.setMarketInqCusVo(marketInqCustomerVo);
}

Using a testing interface

I still have another problem with my beast function, null checks!..

if (blalba != null){...}

AFAIK, this is tricky problem and the solution depends on the circumstances. One option is “if you have repeated checks for a null value, then replace the null value with a null object”.

In my case, it doesn’t fit for my problem. If there are many different classes to be checked, you need to add isNull() operation to the class that you make a null check. I need to modify 5 classes just for that function but 31.533 for the whole system!

In my opinion, there should be a consensus about the necessity of introducing null objects among the team. Maybe, only the subset of the most frequently used null classes will be created. What do you think?

As I need lots of classes and don’t want to modify 31.533 classes, I prefer another option “using a testing interface“. Consider following example:

retrieveOrganizationAddressPhoneInfoResponse = retrieveOrganizationAddressPhoneInfo();
if (retrieveOrganizationAddressPhoneInfoResponse != null) {
   if (retrieveOrganizationAddressPhoneInfoResponse.getAddresses() != null) {
   }
}

Step 1: Create a null interface with no methods defined

interface Nullable { }

Step 2: Implement Nullable in my null objects

class NullOrganization extends RetrieveOrganizationAddressPhoneInfoResponse implements Nullable{

}

Step 3: Test for nullness with the instanceof operator:

retrieveOrganizationAddressPhoneInfoResponse = retrieveOrganizationAddressPhoneInfo();
Address address = retrieveOrganizationAddressPhoneInfoResponse.getAddresses(); 
if ( !(retrieveOrganizationAddressPhoneInfoResponse instanceof Nullable) && !(address instanceof Nullable)) { 
   ... 
}