Refactoring (#75)

by Pat Eyler

This week's quiz is a bit of a departure from the normal. Instead of submitting different implementations of the same code, we'd like you to submit different implementations of the same process -- Refactoring.

Refactoring is the art of improving the design of existing code, without changing it's functional behaviour. It is well documented in the book 'Refactoring' by Martin Fowler, and on his website:

Refactoring Home Page

The quiz this week is to submit refactorings of code you use -- whether your own code, a library or application for RubyForge, or even something from the Standard Library. Any submission should implement a refactoring from:

Refactorings in Alphabetical Order

or other citable sources, e.g.:

Refactoring: Extract Mixin

Each Submission should follow this outline:

Refactoring name (and citation if needed)
Original code
Explanation of the purpose and mechanics of the refactoring
New code
(optionally, unit tests created/used to verify the code)

Submissions will be combined into an online catalog of Ruby Refactorings, probably on the RubyGarden wiki.


Quiz Summary

Hopefully this quiz caused some of us to read Martin Fowler's Refactoring. I know it did exactly that for me, though it has been on my want-to-read list for some time now. I'm half-way through it now and I can only recommend everyone reading this pick up a copy immediately.

Each of this week's solutions came with its own write-up so there isn't a lot left for me to dissect. Instead, I'll take this chance to talk a little about the most interesting part of this exercise to me.

It's my opinion that a large majority of the refactoring patterns were designed with the static languages in mind. I'm very intrigued by how a language like Ruby changes the game. Let's me see if I can give some examples.

First, I mentioned in my write-up that part of my refactoring called for making a method abstract. After some thought, I decided that meant deleting the method in Ruby. (See my solution for the reasoning.)

Here's another thought: many of the refactorings tell you to declare a variable as final, to ensure that it isn't changing. What's the Ruby equivalent to that? I don't think we have one. We can #freeze the object, but that won't help us if the variable is reassigned. I don't see a good answer there.

But there are two sides to every coin and we have some nice advantages. For example, Extract Method talks a lot about how the local variables can complicate extraction, especially if many of them are changed by the extracted code. This is much less of an issue in Ruby though, since we can have the extracted method take a block and use the variables in there however we need. In fact, this means we can use Extract Method quite a bit more often, since the code we pull just needs to be close to similar and we can handle the differences in the block.

Dave Burt was also thinking along these lines and went all the way to introduce some Ruby centric refactorings. Here's an example:

Refactoring 2: Remove Unused Scope
/You have a begin...end block that introduces a scope that is unused./
*Remove the unused scope.*

Thus:

def foo
begin
bar
rescue
baz
end
end

Becomes:

def foo
bar
rescue
baz
end

I would love to see many more of these. Perhaps we could come up with a Replace Similar Methods with Metaprogrammed Definitions and a Convert a Family of Methods to a Single method_missing() Call recipes. Food for thought.

A big thank you to all who supported this deviation from our usual fun and games. I think Pat hit on a super important topic here and we would all do well to learn more about it.

Tomorrow, Matthew Moss is back with a quiz about txet smnraiclbg...