Jan 27, 2005

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.

Jan 11, 2005

No New Year Resolutions for Me

Every time a new year dawns and people come up with resolutions. Though I am not the kind of person who can do it with resolutions. I cannot decide anything at the spur of a moment and go for it. I though must point out that I apply this constraint to professional matters ;-)

The problem I find with resolutions is that there is not sufficient thought thats gone behind it. Its like a fashion statement. With time it fades away. On the contrary, I find it much better to measure what I have done every month or so. And very strictly every quarter. That is strictly to gauge where I am versus where I could be.

Also I use the new year as a year end review to find out if I have met the milestones I set. But these milestones are not resolutions. They are strictly thought out, refined and finalized. I typically do that in the last 2 weeks of Dec with the feedback of monthly, quarterly reviews. So its thought and planned. Not resolution.

Regarding reviews I keep them plain simple. I use an excel document with worksheets for personal/professional targets. Personal targets are also professional activities but outside the domain of my daily work! I keep checking my status, keep purging out those activities which may not be worth it. I am pretty much using XP feedback techniques. Just one excel sheet and regular positive thinking has helped me.

So if your resolutions are backed up with thoughts and flexible planning then great. So also I would say milestones without thoughts / feedback are worthless. Thus its your thinking and flexible planning that matter.

Have a Great Year Ahead.