« Home | No New Year Resolutions for Me » | Learning Tips » | IIM CAT 2004 - The wounded lion roars back » | Personality Types » | Is Xtreme Programming the next Strategic Inflectio... » | I "WILL" » | Reading Better and Faster » | I am blogging »

Why refactoring helps?

Take a look. I had this requirment:
We conduct polls (which can be looked at like permanent polls). We gather monthly statistics. We want to :
  • find out votes cast till a month say Jun 2005
  • find out votes cast for a quarter of a finanical year say Quarter 1 for 2004 - 2005
  • find out votes cast for a half yearly of a financial year say half year 1 for 2004 - 2005
  • find out votes cast for a fin year say 2004 - 2005.
Then the user told me "hey we capture votes in various forms, for me the most erratic turned out to be Money". So I started with that.

To start off I started without implementing any classes but with junit test cases which looked like:

PollTest {
setUp() {//this also came out of refactoring but I wont go into it
newPoll = new Poll("X");
aprData = new MonthlyData("apr 2004", new Money(265, USD)); //This money looks good //:-)
mayData = new MonthlyData("may 2004", new Money(335, USD));
//and so on for mths till Mar 2005
newPoll.addMonthlyData(aprilData);
newPoll.addMonthlyData(mayData);
//and so on for mths till Mar 2005
}

testOneMthBalance() {
assertEquals(new Money(265, USD), newPoll.getBalanceTill("apr 2004"));

}

testTwoMonthsBalance(){
assertEquals(new Money(600, USD), newPoll.getBalanceTill("may2004");
}

//and so on for newPoll.getQuarterlyBalance(quareterNo, financialYear)....
}

Note: I write one test method , see the red bar in junit, then implement that and see the green bar in junit. After writing methods for all such balances reqd, my code in poll class looked like:

Poll {
List monthlyData;

public Money getBalanceTill(Date mth) {
Iterator e = monthlyData.getIterator();
Money balance = new Money(0, USD);
while(e.hasnext() ) {
MonthlyData eachData = (MonthlyData)e.next();
if(! eachData.getMth().after(mth)) {
balance = balance.add(eachData .getVotes());
}
return balance;
}
}

//Note below: I look at a financial year as a range, hence the Range pattern.makes life easy
public Money getQuarterlybalance(int quarter, DateRange finYear) {
Iterator e = monthlyData.getIterator();
Money balance = new Money(0, USD);
//This method gives me range(apr 2004 to jun 2004) for quarter = 1 of 2004 - 2005 fin
//year
DateRange quarterRange = financialYearUtility.getQuarter(quareterNo, finyear);
while(e.hasnext() ) {
MonthlyData eachData = (MonthlyData)e.next();
if(quarterRange .contains(eachData.getMth()) {
balance = balance.add(eachData .getVotes());
}
return balance;

}

public Money getHalfYearlyBalance(int halfyearno, DateRange finYear) {
Iterator e = monthlyData.getIterator();
Money balance = new Money(0, USD);
//This method gives me range(apr 2004 to sep 2004) for half year= 1 of 2004 - 2005 fin
//year
DateRange halfYearRange= financialYearUtility.getHalfYear(halfyearno, finyear);
while(e.hasnext() ) {
MonthlyData eachData = (MonthlyData)e.next();
if(halfYearRange.contains(eachData.getMth()) {
balance = balance.add(eachData .getVotes());
}
return balance;
}


public Money getYearlyBalance(DateRange finYear) {
Iterator e = monthlyData.getIterator();
Money balance = new Money(0, USD);
while(e.hasnext() ) {
MonthlyData eachData = (MonthlyData)e.next();
if(finYear.contains(eachData.getMth()) {
balance = balance.add(eachData .getVotes());
}
return balance;
}

}

PS: This is also extracted sample code.

  • After all this pain, junit was turning green. I had small classes like Poll, MonthlyData, DateRange, FinancialYearUtility (to give me ranges I wanted), DateRange, Money. I was happy. Nah.
  • Whenever I looked at above four methods I wasnt. I knew I could reduce them. But couldnt. Then suddenly it struck me. Look closely. All four methods iterate the monthly list.
  • For those mths whcih fall within a range, it gives me the balance. I started off with getBalanceTill(mth) and all following methods pretty much did the same.
  • For getBalanceTill method, I figured out its nothing but corresponding fin year start mth - to given mth. Bingo. Now my 4 methods looked like this:

Poll {

private Money getBalanceFor(DateRange range) {
Iterator e = monthlyData.getIterator();
Money balance = new Money(0, USD);
while(e.hasnext() ) {
MonthlyData eachData = (MonthlyData)e.next();
if(range.contains(eachData.getMth()) {
balance = balance.add(eachData .getVotes());
}
return balance;

}

public Money getBalanceTill(Date mth) {
return getBalanceForRange(financialYearUtility.getRangeFor(mth));
}

public Money getQuarterlyBalance(int quarterNo, DateRange finYear) {
return getBalanceForRange(financialYearUtility.getQuarter(quarterNo, finYear));
}

public Money getHalfYearlyBalance(int halfYearlyNo, DateRange finYear) {
return getBalanceForRange(financialYearUtility.getHalfYear(halfYearlyNo, finYear));
}

public Money getYearlyBalance(DateRange finYear) {
return getBalanceForRange(finYear);
}
}

Note:
  • I was very happy because it reduced lines of code and made code more simpler. My pair partner (a learner ;-) aruged getYearlyBalance and getBalanceFor(range) methods are same. I said yes.
  • But I went for transparency than reducing even more lines of code because that would make the code confusing.
  • He asked how and I said if year mthd does the task of calculating balances then halfyearly method looks like:

public Money getHalfYearlyBalance(int halfYearlyNo, DateRange finYear) {
return getYearlyBalance(financialYearUtility.getHalfYear(halfYearlyNo, finYear));
} // i will go nuts if i read something like this


So you got to judge the values of refactorings. First up, refactoring is letting u enable DRY (Dont Repeat Yourself) principle. At the same time keep looking for writing maintainable code.

Surprising aint it, that when I get into projects which force me to write procedural code in Java, I SUCK BIG TIME> but i m happy (that means I cant move away from OO code :-). Some things in life cant be explained.

This learner friend of mine remarked "No probs if you are arrogant about your OO Skills. Be like that. Its FUN" :-)

Anyone wants to expand Money class and handle all conversions is welcome. I am more than glad. :-P. Anyone wants to learn Refactoring, TDD give me a shout.