Automated refactoring tools are great, but sometimes the tool doesn’t make the best choice. We can often nudge the tool to a better choice.
Why Bother?
Why bother nudging? Couldn’t you do the refactoring manually just as quickly? You could do it manually, but I prefer to lean on the tool. The tool is more likely to get it right, independent of my mood and hunger level.
Working manually isn’t that fast as people expect: by the time you create the boilerplate and fix any typos, you’ve come out well behind.
Extract Method Speaks
A refactoring tool analyzes the data flow of the code it extracts. This influences the parameters it chooses. Watch what’s chosen: Does it pass values you didn’t expect? Does it not pass values you did expect?
These are clues. When a new method doesn’t pass arguments, it’s telling you, “I depend on fields and/or global variables.” Is that what you want?
The initial Extract Method isn’t everything – you may be able to adjust the method after extraction, e.g., with an Introduce Parameter refactoring. But don’t ignore what the refactorings are trying to tell you!
Fixing a Constructor
One of these cases occurs often in constructors, especially when a new type wants to emerge.
Example: Perhaps you’ve seen the minesweeper game. The game begins with the computer hiding some mines randomly on a grid.
We can imagine constructor pseudocode something like this, hiding 10 mines on an 8×8 grid:
class Game {
var board : [Int]
: // more fields
constructor() {
board = Array(repeating: 0, count: 63)
var randomPositions = generatePositions(10, 0...63)
randomPositions.forEach { mine in
board[mine] = -1
}
0...63.forEach { position in
board[position] = countMines(board, position)
}
: // more initialization
}
:
}
Extract Method
Complicated setup suggests that you may be missing a type.
If you extract the code shown, the tool probably suggests a method with no arguments or return values. That improves the readability, but relies on the mid-level side effect of updating a field.
Instead, I’d rather have a method that returned the created board, called something like this:
board = createBoard(cells: 64, mines: 10)
This new method should be standalone – no fields involved, only parameter values.
A Manual Approach
Suppose board
is a field, not a parameter or local variable. (If board
is a variable or parameter, rename it to get it out of the way.) Make this modification at the start of the sequence of statements:
board = .... => var board = ...
Add this line to the end:
self.board = board
Now, select the code from “var board
” through the end of the original code (not the new assignment), and extract the method. You should now be able to have the desired assignment “board = createBoard(...)
“, perhaps needing to Inline Variable to get it.
Side note: “self.” or “this.”
Some code styles require that every field reference be prefixed with “self.
” (or “this.
“). I used to be in the camp of “I wouldn’t bother, but I’ll go along with the house style.”
Having used this refactoring a few times, I find that style guideline to be an active hindrance.
If your code uses the “self.board
” style, you’ll need to remove the “self.
” references before applying the refactoring.
To go a little further, codes styles that use different naming standards for fields, constants, local variables, etc. are making the code less resilient to change.
You Can’t Always Get What You Want
I can’t offer you a magic formula that will always bend the refactoring tool to your will. For example, I had code something like this:
x = array[index1].at(index2)
I really wanted to call it like this:
x = fn(array, index1, index2)
I don’t say it’s impossible to make the tool extract how I want, but I can say I didn’t do it in my first three tries.
Conclusion
It’s easy to let the tool drive the decision about a refactoring, but sometimes we can influence it to go where we want. We saw a simple technique that used a local variable to override a field name. Other situations may need other approaches. That’s OK; sometimes we need small techniques to boost the power tools.
Further Reading
“Refactoring” [tag] – https://xp123.com/articles/tag/refactoring/