The Compulsive Coder, Episode 5: Extract Method Flow

by Jeff Langr

September 07, 2016

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:

refactorMenu

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.

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”.


Share your comment

Jeff Langr

About the Author

Jeff Langr has been building software for 40 years and writing about it heavily for 20. You can find out more about Jeff, learn from the many helpful articles and books he's written, or read one of his 1000+ combined blog (including Agile in a Flash) and public posts.