I’ve been trying to get better at refactoring lately. To do that, I’m digging into my old code from the first months of gSchool and applying strategies from Refactoring Ruby by Jay Fields, Shane Harvie, Martin Folwer, and Kent Beck.
Just a couple of days ago I posted about trying out Replace Method with Method Object. Almost immediately afterwards, I read about two more strategies that seemed like they would fit well with the same refactoring example, so I thought I’d give them a try: Introduce Named Parameter and Introduce Class Annotation.
Introduce Named Parameter
According to the book, naming your parameters is a good thing to do if you want to improve the readability of a method. Using it prevents the work of having to go searching through dependency objects to figure out what’s going on. It works easily with the new BeadDistributor
class I created in my last article. Here’s that code again:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
|
The most important parts here will be the attr_reader
s and the initialize
method.
Here are the steps for Introduce Named Parameter:
1 2 3 4 5 6 7 8 9 |
|
As always, I started with the tests. They were still passing after my last refactoring adventure:
For the purposes of this example, I wanted to make all of the parameters required, so I skipped steps 1 and 2.
Here are the relevant parts of the code again:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
Step 3: Replace the params in the calling method with key/value pairs.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Step 4: Modify the params taken into the receiving object to take a hash.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Step 5: Test.
The test is failing, because I haven’t modified it to send a hash instead of the individual params. After fixing this, it passes again.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
At this point, everything’s working. But why did I do it? If anything, it just added duplication to what was already there. Here’s why: you can combine it with the Introduce Class Annotation strategy to DRY up object initialization.
Introduce Class Annotation
Although I’ve heard from many developers more experienced than I am that metaprogramming is generally a bad idea, I also get the impression that many of them consider doing it anyway a guilty pleasure, particularly in non-production code.
However you feel about it, gird yourself, because we’re heading into a little bit of define_method
territory.
I’m going to use the same annotation the book gives as an example. It’s a way of defining a helper along the lines of attr_reader
, attr_writer
, and attr_successor
. In it, you wrap all of the object initialization code into a neat new method called hash_initializer
.
Here are the steps:
1 2 3 4 5 6 7 |
|
Here we go.
Step 1: Declare the new class annotation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
Step 2: Convert the method to a class method. I also called the hash_initializer
at the top of the BeadDistributor
class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
Step 3: Test.
All green!
Step 4: Extract the class annotation into a module.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
To me, the module you get at the end is like the gift that keeps on giving. Now I have a cool utility modult that I can use in other objects that take a hash of parameters. Right away, I can think of a program where I can use it in at least six classes.
Running the tests once more, everything is still cool.
Conclusion
As I got toward the end of the fifth chapter, Refactoring got pretty deep into some method_missing
and define_method
metaprogramming madness. While I can’t say I’m very excited to use these things in my day-to-day refactoring, it’s nice to have them there as a reference in case I ever need them. If nothing else, tricks like the ones I tried out here help me think about the relationships between classes in new ways. Next week, I’ll try some strategies from the next chapter: “Moving Features Between Objects”.