The Compulsive Coder, Episode 5: Extract Method Flow

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, “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, which 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 to derive threeDaysLate 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 method. Undo!

A better sequence:

  1. Extract to local variables any expressions we want as arguments.
    1. 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);
      
    2. 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);
      
  2. Extract the method. First select either the whole line, or just select the right-hand side of 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;
       }
    
  3. Inline the arguments.
    1. 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.
    2. 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, I find the local variable threeDaysLate personally useless, so I also 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.


Previous Compulsive Coder blog entry: You Create, It Assigns
Next Compulsive Coder blog entry: this Duplication is Killing Me

It's my site! Check out the About page for more about me, or follow me on Twitter at @jlangr.
  1. Pingback: Langr Software Solutions » The Compulsive Coder, Episode 4: You Create, It Assigns

  2. jeff bay Reply

    Alternatively to “extract locals, extract method, inline”, I usually go the opposite route: “extract method, inject parameters, inline/rename/etc”.

  3. Pingback: Langr Software Solutions » The Compulsive Coder, Episode 6: this Duplication is Killing Me

Leave a Reply

*

captcha *

Atom