To be a refactoring wiz in Eclipse or IDEA, you don’t need to memorize dozens of keystrokes. Combined with a few mega-important keys (particularly Ctrl-1 in Eclipse or Alt-Enter in IDEA, “quick fix”), seven fairly mnemonic keystrokes will supply the bulk of your refactoring muscle. In Eclipse, they happen to be the top seven on the refactoring menu:
Your version of Eclipse won’t have a key assignment for Extract Constant. It’s one of the rare keys I custom-assign, and K
makes sense if you remember enough high school math.
There are about 20 more refactoring menu items in Eclipse, yet I use most of those so infrequently that I don’t find value in trying to memorize the keys to which they’re bound. I use Convert Local Variable to Field often enough, but rather than assign and remember a keystroke, I simply delete the type declaration and use Ctrl-1 to define the field.
I use these “Super Seven” shortcuts frequently throughout a typical coding day. They (a) minimize the amount of typing I do (and subsequently reduce the opportunity for mistakes) and (b) minimize the amount of mouse clicking I do. Mouse clicking is about an order of magnitude slower and worse for my wrist.
I’m amazed at how often I’m able to accomplish some fairly significant refactoring by solely using combinations of the Super Seven.
One such ultra-common sequence is the extract method flow, a bread-and-butter operation for any developer.
Let’s do a bit of extracting against the following test method:
@Test
public void answersDaysLateWhenReturnedAfterDueDate() {
holding = new LibraryHolding(THE_TRIAL, BRANCH_EAST);
holding.checkOut(TODAY);
Date threeDaysLate = new Date(holding.dateDue().getTime() + 3 * MS_IN_ONE_DAY);
int daysLate = holding.checkIn(threeDaysLate, BRANCH_EAST);
assertThat(daysLate, is(3));
}
We want to isolate the ugly code needed to derive the threeDaysLate
value into a helper method, perhaps named addDays
. But if we highlight the entire line and simply do Extract Method (Cmd-Option-M), Eclipse doesn’t quite do what we want:
@Test
public void answersDaysLateWhenReturnedAfterDueDate() {
holding = new LibraryHolding(THE_TRIAL, BRANCH_EAST);
holding.checkOut(TODAY);
Date threeDaysLate = addDays();
int daysLate = holding.checkIn(threeDaysLate, BRANCH_EAST);
assertThat(daysLate, is(3));
}
private Date addDays() {
Date threeDaysLate = new Date(holding.dateDue().getTime() + 3 * MS_IN_ONE_DAY);
return threeDaysLate;
}
We want holding.dateDue()
and 3
to be arguments to the new method. Undo!
A better sequence:
-
Extract to local variables any expressions we want as arguments.
-
Extract the method.
-
Inline the arguments.
Let’s dig into these steps one by one.
Extract to local variables any expressions we want as arguments.
Highlight holding.dateDue()
and press Cmd-Option-L (Extract to Local Variable). Change the variable name to simply date
. The result looks like this:
Date date = holding.dateDue();
Date threeDaysLate = new Date(date.getTime() + 3 * MS_IN_ONE_DAY);
Similarly highlight the 3
and extract it as a local named days
:
Date date = holding.dateDue();
int days = 3;
Date threeDaysLate = new Date(date.getTime() + days * MS_IN_ONE_DAY);
Extract the method
First select either the whole line, or just select the right-hand side of the statement that does the assignment to threeDaysLate
. Then press Cmd-Option-M. Type in the new method name of addDays
. The result:
@Test
public void answersDaysLateWhenReturnedAfterDueDate() {
holding = new LibraryHolding(THE_TRIAL, BRANCH_EAST);
holding.checkOut(TODAY);
Date date = holding.dateDue();
int days = 3;
Date threeDaysLate = addDays(date, days);
int daysLate = holding.checkIn(threeDaysLate, BRANCH_EAST);
assertThat(daysLate, is(3));
}
private Date addDays(Date date, int days) {
Date threeDaysLate = new Date(date.getTime() + days * MS_IN_ONE_DAY);
return threeDaysLate;
}
Inline the arguments
Click on (no need to select) either occurrence of the date
local variable (either the declaration+assignment, or its use as an argument to addDays
). Press Cmd-Option-I (Inline). You’ll receive a confirmation dialog asking whether or not you want to Inline 1 occurrence of local variable date
; simply press enter.
Similarly, click on either occurrence of the days
local variable, and press Cmd-Option-I to inline it. The result:
@Test
public void answersDaysLateWhenReturnedAfterDueDate() {
holding = new LibraryHolding(THE_TRIAL, BRANCH_EAST);
holding.checkOut(TODAY);
Date threeDaysLate = addDays(holding.dateDue(), 3);
int daysLate = holding.checkIn(threeDaysLate, BRANCH_EAST);
assertThat(daysLate, is(3));
}
private Date addDays(Date date, int days) {
Date threeDaysLate = new Date(date.getTime() + days * MS_IN_ONE_DAY);
return threeDaysLate;
}
In addDays
, the local variable threeDaysLate
adds no value, so you can inline it.
What I love about extracting small abstractions like this is that they open my eyes to additional opportunities to clean things up. It’s now obvious that the method we created belongs elsewhere, perhaps in some sort of date utility class where it broadcasts its existence and thus entices folks to reuse it. What also becomes evident is that the local variable threeDaysLate
no longer adds much value. We inline it:
@Test
public void answersDaysLateWhenReturnedAfterDueDate() {
holding = new LibraryHolding(THE_TRIAL, BRANCH_EAST);
holding.checkOut(TODAY);
int daysLate = holding.checkIn(addDays(holding.dateDue(), 3), BRANCH_EAST);
assertThat(daysLate, is(3));
}
Three steps: extract locals, extract method, inline. No direct typing in the editor. Repeat and ingrain.
Related posts
Pingback: The Compulsive Coder, Episode 1: The Stub Comment
Pingback: The Compulsive Coder, Episode 2: Syntax Coloring
Pingback: The Compulsive Coder, Episode 3: Typing Isn’t the Bottleneck
Pingback: The Compulsive Coder, Episode 4: You Create, It Assigns
Pingback: The Compulsive Coder, Episode 5: Extract Method Flow
Pingback: The Compulsive Coder, Episode 6: this Duplication is Killing Me
Pingback: The Compulsive Coder, Episode 7: Please AAA
Pingback: The Compulsive Coder, Episode 8: You Might Not Like This
Comments
jeff bay October 19, 2016 at 7:41 pm
Alternatively to “extract locals, extract method, inline”, I usually go the opposite route: “extract method, inject parameters, inline/rename/etc”.