Activity 2-2 Introduction to refactoring

Refactoring can therefore be thought of as serving two main uses:

  1. Cleaning up the code to make it more friendly to future “visitors”. We will call this the boyscout rule: Try to leave the code a little better than you found it.
  2. Preparing the code for the addition of some future elements. We must be careful with this second one, and not do it willy-nilly without a concrete idea of what new elements we want to implement. Trying to anticipate all future requirements and preparing the code for them results in overly convoluted code that is hard to work with; and it turns out at the end of the day that most of the features you thought would be added were in fact not added. This situation is typically described by the acronym YAGNI for “you aren’t gonna need it”. The key underlying goal should always be to keep the design simple.

Refactoring takes many forms, and a more precise list of these refactorings is described on this page as well as the refactoring book.

As an example of this process, consider the extremely straightforward but quite long-winded solution to the grade-processing activity on the first page of the handout.

Group activity: Discuss what features of this code make it hard to read, and possibly hard to change in the future. Do this before reading on.

So let’s list a number of problems here:

Phase 1: Renaming and method extraction

We can start with some simple changes:

At this point our code looks as in the second page of the handout. Notice how much more readable the main function’s operations are.

Phase 2: Class extraction, moving elements around

Looking at all these, a couple of things stand out that suggest creating some new classes:

Let’s see how we might go about creating these classes.

Our code now looks like the third page of the handout.

Now we proceed with our second class extraction. It looks like it might be nice to have the concept of a grade as more than a single letter, namely an entity that has some functions. Maybe later we can turn it into an enum, but for now it would be good to simply have a Grade class.

We will also handle the processing steps. We effectively want to replace all uses of the scanner with uses of a newly-created Processor, with the scanner as part of its constructor. In order to achieve this, we’ll have to perform a step that by itself does not appear useful, namely we’ll extract the whole while loop into a single new method. This is a temporary step so that we have a place where we can perform “Extract object”.

And now we see the final form of our code, nicely separated into four classes. Notice how simple the processGrades method has become: