Wednesday, 14 June 2017

An introduction to Refactoring Java in IntelliJ using RestMud as an Example


TL;DR Never too late to refactor. Do it in small chunks. Protected by tests. Using IDE to refactor.





My RestMud game grew organically, I do have a fair bit of Unit testing, but I also perform more integration testing than unit testing.

The good thing about this approach is that the integration testing represents requirements i.e. in this game I should be able to do X, and my integration tests at a game engine level create test games and check conditions in there.

These tests rarely have to change when I amend my code.

The side-effect of this type of testing is that the classes don’t have to be particularly good, so I have a lot of large classes and not particularly good organisation.

I’m now refactoring the classes and organising the code to have 4 main sections:

  • Game Engine
  • Games
  • API
  • GUI

At the moment I’m concentrating on the Game Engine.

I have a large main class called MudGame and I’m splitting that into smaller classes now.

Refactoring from Map to POJO


As an example my MudGame used to have a Map for locations, and collectables and messages.

This meant that I had 4 or 5 methods for each of these collections in my MudGame, I have now only a few high level methods in Game and most of the code has moved to the Locations, or Collections object.

As I was doing this I had to make a decision, do I make a public final field, or do I create a private field, with an accessor method.

I initially chose public final and amended the code, and then changed my mind to have an accessor method.

I don’t worry too much about this because it is easy to use IntelliJ refactoring to rename and wrap fields in accessor methods.

private Map<String, MudLocation> locations = new HashMap<>();

To an object that manages locations, which contains all the code methods that were on MudGame

public final Locations gameLocations = new Locations();

I chose to make the field public initially, then I refactored using “Encapsulate as Method”:

private final Locations gameLocations = new Locations();

and

    public Locations getGameLocations() {
        return gameLocations;
    }

Refactoring Methods to Inline Code


Sometimes when I have a method that is small and doesn’t really add any value because I delegate all the functionality off to another Object, I might choose to inline it:

    public MudLocationObject getLocationObject(String nounPhrase) {
        return getLocationObjects().get(nounPhrase);
    }

When I inline this then anywhere in the code that had:

MudLocationObject locationObject = game.getLocationObject(thingId);

Becomes:

MudLocationObject locationObject = game.getLocationObjects().get(thingId);

Some Tips for Refactoring


  • requirement level tests should not have to change during refactoring
  • make sure you have tests before you refactor
  • don’t worry too much about naming or field/method choices during initial coding because it is easy to refactor later
  • use IDE refactoring where possible
  • when code gets ugly, get refactoring
  • refactor in small chunks, keep chipping away,
  • refactor low hanging fruit first as it makes it easy to see what comes next
  • group code together to loosely organise prior to refactoring into new classes
  • Refactor Classes to represent semantics as well as helping organising code

It’s never too late to refactor your code.


Bonus Youtube Video


See also the accompanying YouTube Video:



In the video you’ll see:

An introduction to Refactoring Java in IntelliJ with a live demo using RestMud Game. I talk you through what refactoring is, and show examples of in built refactoring functionality in IntelliJ.

  • An introduction to refactoring
  • Basic Refactoring techniques and approaches explained
  • Refactoring from fields to methods with “Encapsulate Field”
  • Run tests after each refactoring
  • Check in code to version control frequently to allow reverting if things go wrong
  • Demonstration of refactoring
  • Explanation of intermittent Unit Test Execution
  • Sometimes as we refactor we discover we are creating duplicate code. When that happens, stop and decide if the existing code is good enough.
    -Try to avoid creating code that you aren’t using yet. You have to maintain it, and there are no tests,
    a nd you probably won’t use it in the future anyway!
  • Refactoring to Inline methods to remove methods completely. Remove the method and replace invocations
    with the code in the method
  • Reflect on your refactoring. Time to stop? Good enough to checkin? More to do?

No comments:

Post a Comment