Summary
A refactoring exercise tickles an old memory and triggers a new connection

Advertisement

I discovered something very interesting today. I was writing some new
code that I intended to have re-use some existing code, but some
refactoring was necessary first. The existing code was a method that
did three things:

isolate a block of text,

parse the block (with some exception handling code thrown in as
well),

run an associated function on the parsed components.

Previously there was only one method that was a client for this code.
Now there would be two, and the code would have to be generalized
accordingly.

The new client of this code already had an isolated text block, and
just needed the block parsed. I first moved the parsing code to the
new method, without the text block isolating code. I figured I'd move
step 3 (running the associated function) up to the calling method
while I was at it, since it seemed to be an independent step.

When I was done, the new method needed to be passed just about every
parameter there was, and had to return a bunch as well. I had to add
some code to handle all these parameters. Although the new method was
now separated from its former neighboring code, the parameters and
return values kept it all too tightly coupled together. That smelled
bad.

After taking a break, I returned to the task. I found that the
parsing code really needed to know the results of isolating the text
block, but the exception handling needed higher-level details that the
parsing code really didn't care about. By moving the exception
handling back into the original method, and the text block isolating
code into the new parsing method, I was able to reduce the number of
parameters required (and values returned) by the new method to
reasonable levels. In the end, the original client code was restored
to its initial state, the original method and the exception handling
was drastically simplified, the new method became nicely generic, and
all the extra code disappeared.

Sometimes, code seems to have a natural optimum arrangement where
parameters are minimized and elegance is maximized.

The completion of this small task tickled an old memory. This
refactoring felt just like working out symbolic algebra problems in
high school math class, such as multiplying polynomials: first expand
all the factors, then combine like terms and simplify. When
refactoring this code, it grew in size and complexity before it could
shrink and become simpler. And just like in algebra, the intermediate
steps are just as important as the end result -- you've got to show
your work to get full marks.

Meanwhile, the unit tests proved their worth yet again. But that's
material for another day...