Sunday, April 19, 2009

Constant Refactoring: A Practical Benefit

As I program, I continually refactor the code I'm in. It's habit at this point. I rarely get the opportunity to do a massive change, but even the basic work I do — removing duplicated code, generalizing logic, dividing up large functions, and so on — reaps unexpected benefits.

Refactoring partly makes code more maintainable, but its main purpose is to make the code flexible enough to respond to new design requirements. I try to code on the assumption that change will come, but if I could predict the exact direction of every future change and thus craft a perfect design, I'd be writing this from my French Chateau.

Here's a simple example I recently found in my iPhone app.

One of the tabs lets you move between dates, one at a time, to see your schedule for that particular date. In my initial design, I didn't let you change dates because that view exists to show you what's going on today. But I quickly realized that I wanted to at least see what tomorrow would bring.

When I made that change, I had two buttons that would switch to either the next date or the previous date. They each called separate methods — nextDay and previousDay — that were fairly similar to each other. I was working through the basic functionality, so I didn't register the commonality. I think this is common enough: You're so focused on one thing that you don't notice something else.

On a later pass through the code, fixing something else, I realized how similar the methods were, and I wrote a new method that would just adjust the date based on some time interval. I gutted nextDay and previousDay and replaced them with calls to the new method that passed either 86400 or -86400. (The number of seconds in a day.)

After that, one of my testers suggested that I let the user swipe across the date to move to the next or previous day, depending on the direction. Once I figured out the gesture system for the iPhone, I just made each swipe call either of the two appropriate methods, which in turn called into that common method.

But then I realized that I needed to add animation to show the user what was happening. I put the animation logic into the generalized method and made it smart enough to know that a negative number meant animate one way, while a positive number meant animate the other way (animate from the left in the first case, because you're moving to the previous day, and animate from the right in the second). That handled the animation for the two cases.

A couple of my testers asked for the ability to jump to an arbitrary day. I decided to look into implementing that this morning, and once I opened the code, I realized it would be a snap. I bring up a UIDatePicker, the "choose from a list" UI in the iPhone, and then just pass the time interval between the user-selected date and the current date to that one common method. The logic is the same, and the code animates appropriately: If you jump to some arbitrary, far-future date, the screen wipes from right to left, as you'd expect. Aside from bringing up the date picker, I wrote exactly one line of code to enable the feature.

For this simple example, I probably would have found my way to the same end result: an all-purpose method for jumping to any given date. But the fact that I had refactored earlier — just because — made the solution obvious and quick.

When I started this feature, you couldn't switch dates at all. Piece by piece, I built the infrastructure so that now you can jump to any date with gestures and animations. That wasn't the original design, but constant refactoring allowed me to eventually support this radically new direction with a minimum of "from scratch" code.

No comments:

Post a Comment