Wednesday, May 13, 2015

A reader's review on Clean Coding Techniques book

I have been reading "Clean Code - A handbook of Agile Softward Craftsmanship" by Robert Martin for few days. I had read Refactoring techniques by Martin Fowler earlier. I observed that such books or those concepts are not as popular or as talked about as Design patterns or Algorithms. Often you will hear discussion happening on desing patterns. Let it be a high level design decision or an interview. However, functions, comments, formatting, namings etc. which are building blocks of writing code, are not taught as often.
Most interesting thing with "Clean Code" book is that it talks about how to write code. Writing code is normally learnt either by experience or by code reviews from senior. "Clean Code" fills up that gap by providing various guidelines to write clean functions, comments, formatting, namings and other building blocks of code.

I found below guidelines extremely useful in day to day coding. Though I have been following few of them earlier, book helps to bring them to together in some context and all at one place.
  • One level of Abstraction per function:
  • Output Arguments
  • Don’t Return Null

I would like to share one example following these clean coding guidelines. Function should be written such that it has all details at one level of abstraction. Having some parts with details and others with abstracted by using inner functions makes reading difficult.

Lets see below example. As you can see getClientInterestRate has some if checks on client type, which are too much of details to reader. Even though how small this function is, one would have to logically partition the code in order to understand it.


public  InterestRate getClientInterestRate(String clientType, BigDecimal amount)
{
 NavigableMap<Long, InterestRate> map = this.interests.get(clientType);

 long lookupValue = amount.longValue();
 if("CORPORATE".equalsIgnoreCase(clientType))
 { 
  lookupValue = lookupValue / MILLION;
 }

 Entry<Long, InterestRate> entry = map.floorEntry(lookupValue);
 InterestRate value = entry.getValue();
 return value;
}

So why not to partition the code to have consistent abstraction as shown below. Here top level function getClientInterestRate is clearly easier to read compared to earlier version. This version of getClientInterestRate would reveal intention of the function much easily.

public  InterestRate getClientInterestRate(String clientType, BigDecimal amount)
{
 long clientAmount = convertAmountToClientBasedUnit(clientType, amount);
 InterestRate clientInterest = getInterestRateForLessThanOrEqualToAmount(clientAmount, clientType);
 return clientInterest;
}


private InterestRate getInterestRateForLessThanOrEqualToAmount(long lookupAmount, String clientType)
{
 NavigableMap<Long, InterestRate> clientInterestRates = getAllInterestsForClient(clientType);
 Entry<Long, InterestRate> interestRateMapEntry = clientInterestRates.floorEntry(lookupAmount);
 if(interestRateMapEntry == null)
  return NO_MARKUP;

 InterestRate matchingInterestRate = interestRateMapEntry.getValue();
 return matchingInterestRate;
}


private NavigableMap<Long, InterestRate> getAllInterestsForClient(String clientType)
{
 NavigableMap<Long, InterestRate> map = getAllInterests().get(clientType);
 return map;
}

private convertAmountToClientBasedUnit(String clientType, BigDecimal amount)
{
 long lookupValue = amount.longValue();
 if("CORPORATE".equalsIgnoreCase(clientType))
 {
  lookupValue = lookupValue / MILLION;
 }

 return lookupValue;
}

No comments: