This tip is something I only learned about in the last year or so of my programming career. But since learning it, I find that it's something I just can't live without and am constantly sharing with other developers. And now it's something that I teach to all of our bootcamp students at our Online Java Programming Bootcamp.

What is Refactoring?

Let's take a look at Wiki's definition of refactoring:

Code refactoring is a “disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior”, undertaken in order to improve some of the nonfunctional attributes of the software. Typically, this is done by applying a series of “refactorings”, each of which is a (usually) tiny change in a computer program's source code that does not modify its conformance to functional requirements. Advantages include improved code readability and reduced complexity to improve the maintainability of the source code, as well as a more expressive internal architecture or object model to improve extensibility.

Now normally I'm not a fan of these definitions because they're a little too technical and attempt to be as “complete” an answer as possible to please everyone. But in this case, I think this is a great definition!

Refactoring is the process of taking existing code and changing it in some way to make it more readable and perhaps less complex. The key thing to note, is that when the code is changed, is does NOT affect the underlying functionality of the code itself. So the changes you're making are literally just to make the code easier to follow by your fellow developers.

Okay, so now that you know what refactoring is all about, how can you go about refactoring the code you already have?

Refactoring Tools

The SpringSource Tool Suite (and Eclipse) provide some great refactoring tools that are easy to use. So, let's take a look at an example of some code that could use some refactoring shall we:

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import org.junit.Test;

import com.howtoprogramwithjava.business.Card;
import com.howtoprogramwithjava.business.Dealer;
import com.howtoprogramwithjava.business.Deck;

public class DealTest
{
  @Test
  public void testDealingOfCards ()
  {
    Deck deck = new Deck();
    Dealer.deal(deck.getDeck(), deck.getPlayers());

    for (int i=0; i<deck.getPlayers().size(); i++)
    {
      assertThat(deck.getPlayers().get(i).getCardsInHand().size(), is(5));

      System.out.println(deck.getPlayers().get(i).getName() + " has the following cards:");
      for (Card aCard : deck.getPlayers().get(i).getCardsInHand())
      {
        System.out.println(aCard.toString());
      }
      System.out.println();
    }
    assertThat(deck.getDeck().size(), is(32));
  }
}

This code was used to unit test the dealing of cards to four players. We are ensuring that every player has 5 cards after the deal, and that there are only 32 cards left in the deck (after 20 cards were dealt out to four players). We also list out what cards are in each players' hand.

Now, as I stated in the beginning, refactoring code isn't about changing its functionality, but to make the code a little bit more readable. So to do this, the first rectoring I want to do is to extract local variables.

How to extract Local Variables

This process will involve us taking a commonly occurring or repeating piece of code and assign it to a variable so that we don't have to keep writing out the code to reference it every time.

Okay, so maybe that doesn't make a whole lot of sense to you just yet, but how about we identify in the above code where we have some repeating references. Look how many times we write out the code deck.getPlayers()… 5 times! Wouldn't it be easier to just assign that to a local variable named players? For sure! So how do we do that?

  1. select the code deck.getPlayers()
  2. right click on it -> choose Refactor
  3. choose Extract Local Variable

Note: All references to the exact ways of using the refactoring tools is only applicable for Eclipse and STS, any other IDE will have a different set of menus to run the same refactoring techniques

Also Note: You can use the shortcut key sequence Alt-Shift-L to extract a local variable

Okay, so now you're presented with a screen that asks you what you'd like to call your new local variable, I just leave it as the default players name and hit enter.

Now let's take a look at our new code:

@Test
public void testDealingOfCards ()
{
  Deck deck = new Deck();

  // here's our new local variable
  List players = deck.getPlayers();
  Dealer.deal(deck.getDeck(), players);

  for (int i=0; i<players.size(); i++)
  {
    assertThat(players.get(i).getCardsInHand().size(), is(5));

    System.out.println(players.get(i).getName() + " has the following cards:");
    for (Card aCard : players.get(i).getCardsInHand())
    {
      System.out.println(aCard.toString());
    }
    System.out.println();
  }
  assertThat(deck.getDeck().size(), is(32));
}

Look at that! Now all of the places in the code where it used to say deck.getPlayers() now points to the players local variable, and it's automatically assigned deck.getPlayers() to this players local variable.

That was pretty painless right? Now, what else can we do to clean things up? I see two references to players.get(i).getCardsInHand(), so let's apply the same technique again to create a local variable called cardsInHand.

How to Extract a Method

Now that you're familiar with extracting a local variable, how about we do the same kind of thing, only with an entire method! This particular refactoring tool will take an entire block of code and turn it into a method. This is very useful for cutting down the number of lines in any given method, and actually helps to self document the method (thus making it more readable).

Let's see if there's any blocks of code in our example that could be extracted into a method…

I see that there's a chunk of code that's just dedicated to outputting details to the console… and this could be thought of as just “noise” in the testing method, so how about we extract it into another method :)

This is a very similar process to extracting a local variable, all you must do is:

  1. select the block of code you wish to extract
  2. right click on it -> choose refactor
  3. choose Extract Method

Note: This can also be done by selecting the code and hitting Alt-Shift-M

Now you'll be presented with an “extract method” screen that asks you to name the method you're about to create. Well, since this is just a block of code that outputs the cards to the console, let's name the method “outputCardDetailsToConsole”. So type that in and hit Enter.

Let's take a look at what we've done now:

@Test
public void testDealingOfCards ()
{
  Deck deck = new Deck();
  List players = deck.getPlayers();
  Dealer.deal(deck.getDeck(), players);

  for (int i=0; i<players.size(); i++)
  {
    List cardsInHand = players.get(i).getCardsInHand();
    assertThat(cardsInHand.size(), is(5));

    outputCardDetailsToConsole(players, i, cardsInHand);
  }
  assertThat(deck.getDeck().size(), is(32));
}

private void outputCardDetailsToConsole(List players, int i, List cardsInHand)
{
  System.out.println(players.get(i).getName() + " has the following cards:");
  for (Card aCard : cardsInHand)
  {
    System.out.println(aCard.toString());
  }
  System.out.println();
}

Would you look at that, now in our test method, all those noisy lines of console output code have been reduced to one line of code that says outputCardDetailsToConsole(players, i, cardsInHand);. The IDE has automatically figured out what parameters need to be passed into the new method that was created and passed in those variables for you. Absolutely painless right? Now the code is much more readable!

Rename Variables

Now there's one shortfall of the “extract method” refactoring tool, and it's that it will often choose non-descriptive variables names for the parameters in the method's signature. So how about we change one of the variable names using our refactoring tools!

I don't like the fact that it has used the variable name i for the player number, so in order to change it, we just:

  1. select the variable we want to rename
  2. right click -> choose refactor
  3. choose rename

You'll see that the variable gets outlined in blue and there's a little dialogue that appears that states “enter new name, press Enter to refactor”. So that's exactly what we'll do! Type in “playerNumber” and hit Enter.

Voila, we've now renamed the variable in the method's signature as well as every occurrence of that variable in the method itself. PAINLESS people!

Note: You can rename a variable using the Alt-Shift-R shortcut sequence.

For all of those shortcut key maniacs out there (like myself), you'll notice that all of your refactoring tools are available via the Alt-Shift-(something) sequence. So just always remember that Alt-Shift is your refactoring friend :)

How to Change a Method's Signature

Now let's say that we've decided that we want to add to our console output. We've decided that we also want to output how many cards are left in the deck… but we don't have the necessary information available to us in our new outputCardDetailsToConsole method. So we'll need to change the method signature and add a new parameter. Now we could do this manually in this case fairly easily by just adding in the parameter ourselves, but, the whole point here is that the IDE will automatically update ALL the references to that method. Imagine a scenario where we have 100 different methods referencing the one we need to change, that would be a big pain in the butt to change manually, but with the “Change Method Signature” refactoring tool, it's a piece of cake.

  1. Select the method's name that you wish to change
  2. Right click -> Choose refactor
  3. Choose Change Method Signature

Now, we want to pass in the Deck object, so in the “Change Method Signature” dialogue that appears:

  1. click the “Add” button
  2. Type Deck in the “Type” column
  3. Give it a good name, like “deck”
  4. For the default value, type in “deck” as well

Note: The “Name” column represents the name of the variable as it appears in the method you're changing. The “default value” column represents what variable name it should put in for where this method is being called, in this case it's being called from our test method, and we know that the test method has a deck variable, so it's safe for us to use this “default value”, if you're unsure what value to use, you could put in null and then go back and change all the references where needed.

Here's a screenshot of what the “Change Method Signature” should look like after you've entered your details:

Change Method Signature

Finally, here's what your code will look like after we make use of this added deck variable:

@Test
public void testDealingOfCards ()
{
  Deck deck = new Deck();
  List players = deck.getPlayers();
  Dealer.deal(deck.getDeck(), players);

  for (int i=0; i<players.size(); i++)
  {
    List cardsInHand = players.get(i).getCardsInHand();
    assertThat(cardsInHand.size(), is(5));

    outputCardDetailsToConsole(players, i, cardsInHand, deck);
  }
  assertThat(deck.getDeck().size(), is(32));
}

private void outputCardDetailsToConsole(List players, int playerNumber,
  List cardsInHand, Deck deck)
{
  System.out.println(players.get(playerNumber).getName() + " has the following cards:");
  for (Card aCard : cardsInHand)
  {
    System.out.println(aCard.toString());
  }
  System.out.println("The deck has " + deck.getDeck().size() + " cards in it.");
  System.out.println();
}

Summary

I use the refactoring tools on a daily basis at my job and I find that it saves me tons of time. Every time I'm pair programming with someone and they happen to see me use one of these tricks, they always stop me and ask me what it was I just did. Then they end up thanking me for saving them so much time… I should seriously start charging for all these tips and tricks ;)

So, use these tricks and remember that the Alt-Shift keys are your refactoring friend! I look forward to seeing some more readable code out there everyone :)

 

Free Java Beginners Course

Start learning how to code today with our free beginners course. Learn more.