Today, in my latest journey on the Refactor Tractor, I’ll be taking a look at a strategy called Move Method.
Move Method is used to help remove duplication. Sometimes, in the interest of encapsulation, it’s a good idea to make an object as unaware of another object as possible. If the first needs to access the second, though, it may have to “reach through” a third object. If this reaching is minimal, it can sometimes be a worthwhile trade off. But when it becomes an ongoing pattern, you can end up with a lot of duplication. And a lot of what my teacher Franklin Webber calls “bad touching”. The Refactoring book points out that “it’s hard to figure out what the right amount of hiding is”, but it’s easy to change your mind with refactoring. Using a combination of this strategy and Hide Delegate, you can always change it again later.
Today I’ll be practicing Remove Middle Man on another example from the Mancala app I was refactoring in my recent post: Refactoring 2 - Replace Method With Method Object.
Here are the steps to Remove Middle Man:
1 2 3 4 5 |
|
That’s fairly cryptic. I think it means: just pass a reference to the distant object into the first object, and access it directly (instead of through object two).
Mancala Game View
There are many poorly-built parts to Mancala. That’s probably because I didn’t test it at all as I was writing it. At the time, I thought I was taking a shortcut. In reality, I was making a big mess that was difficult to understand and deal with. I didn’t realize it at the time, but “TDD’s primary benefit is to improve the design of our code”.
One of my poorly designed classes is called the MancalaGameView. Its job is to draw the beads in their respective pits, and print the winner to the screen, using Ruby Processing. Here’s the problem with my design: I pass in the ‘app’ object, then call through it to get information about the ‘rules’ and ‘model’ objects. Here’s what it looks like:
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 |
|
The calls to app.fill
and app.text
are built into Ruby Processing. Outside of that, I’m not going to get into the details of what some of these methods, like draw_x_beads
and draw_beads_in_player_one_store
. Because the coupling in this app is so high, it would require pulling in too many methods.
Setting Up the Test
I’m going to stub out a few things, so that we can build a test without having to drag in the rest of the app and the Ruby Processing Gem.
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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
|
And now, to build a test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
This test is green. I don’t like to start from green. It’s supposed to be: “red, green, refactor”! I could comment out some code or change game_over?
to false, but that wouldn’t prove anything about the code. Since this is an instance of Test After, and the code is already understood, I’m feel comfortable going forward from green here.
Applying the Strategy
Now, to begin removing the middle man object, app
. There are two classes I’m accessing through app
: Model
and Ruler
. I’ll extract them one at a time, beginning with Model
. The first step is to “create an accessor for the delegate”. I could just do this:
1 2 3 4 5 |
|
But I would rather pass the model into the MancalaGameView directly:
1 2 3 4 5 6 7 8 9 10 |
|
Step two is to change “client” calls so that they use the new accessor instead of calling through the “server” (which is app
in this case). Step three is to test after each method. So here we go:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
The test fails with: wrong number of arguments (1 for 2)
. The problem lies in the test setup. I now need to pass in the model when I create the MancalaGameView
.
1 2 3 4 5 6 7 8 |
|
Ahh, that’s better. Now to repeat the process with the ruler
. Step one: create the accessor.
1 2 3 4 5 6 7 8 9 10 11 |
|
Steps two and three: replace the method calls to use the accessor instead of reaching through app
. Test after each step.
1 2 3 4 |
|
It fails, because I’m not passing ruler
into the test.
1 2 3 4 5 6 7 8 |
|
That gets me back to green. Now to refactor the print_winner
method:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Success! That was pretty easy.
Further Refactoring
Now that I’ve cleaned up the MancalaGameView somewhat, I’m going to refactor a few other things. Along the way, I noticed that print_winner
is pretty ugly, so I changed it to use a hash lookup instead.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
I also decided to use extract method on the loops in draw_all_beads
:
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 |
|
That’s much better. Here’s the final result:
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 |
|
Conclusion
Remove Middle Man was really easy to use. Maybe it’s because I’ve heard Franklin harp on the evils of “bad touching” so much, or maybe it’s just that I’m violating the Law of Demeter, but I start to feel pretty nervous when I see two dots in my method calls. This was a quick and easy refactor that I’m sure I’ll use regularly. Since it sets you up for Dependency Injection, it also opens up the class’s possibilities for Duck Typing and easy mocking.
As I continue to code and refactor, Remove Middle Man will be a vital tool in my tool kit.