Refactoring [Fowler99] is a
structured ,
disciplined method to rewrite or restructure existing code without changing its external behavior, applying small transformation steps combined with re-executing tests each step. Continuously refactoring code is another XP practice and applicable to all iterative methods (including the UP).[3]
[3] Ralph Johnson (one of the Gang-of-Four design pattern authors) and Bill Opdyke first discussed refactoring in 1990. Kent Beck (XP creator), along with Martin Fowler, are two other refactoring pioneers.
The essence of refactoring is applying small
behavior preserving transformations (each called a 'refactoring'), one at a time. After each transformation, the unit tests are re-executed to prove that the refactoring did not cause a regression (failure). Therefore, there's a relationship between refactoring and TDDall those unit tests support the refactoring process.
Each refactoring is small, but a series of transformationseach followed by executing the unit tests againcan, of course, produce a major restructuring of the code and design (for the better), all the while ensuring the behavior remains the same.
What are the activities and goals refactoring? They are simply the activities and goals of good programming:
remove duplicate code
improve clarity
make long methods shorter
remove the use of hard-coded literal constants
and more…
Code that's been well-refactored is short, tight, clear, and without duplicationit looks like the work of a master programmer. Code that doesn't have these qualities
smells bad or has
code smells . In other words, there is a poor design.
Code smells is a metaphor in refactoringthey are
hints that something
may be wrong in the code. The name
code smell was chosen to suggest that when we look into the smelly code, it might turn out to be alright and not need improvement. That's in contrast to
code stench truly putrid code crying out for clean up! Some code smells include:
duplicated code
big method
class with many instance variables
class with lots of code
strikingly similar subclasses
little or no use of interfaces in the design
high coupling between many objects
and so many other ways bad code is written…[4]
[4] See the original and major OO, patterns, XP, and refactoring Wiki c2.com/cgi/wiki for many Wiki pages on code smells and refactoring. Fascinating site…
The remedy to smelly code are the refactorings. Like patterns, refactorings have names, such as
Extract Method. There are about 100 named refactorings; here's a sample to get a sense of them:
Refactoring | Description |
---|---|
Extract Method | Transform a long method into a shorter one by factoring out a portion into a private helper method. |
Extract Constant | Replace a literal constant with a constant variable. |
Introduce Explaining Variable (specialization of Extract Local Variable) | Put the result of the expression, or parts of the expression, in a temporary variable with a name that explains the purpose. |
Replace Constructor Call with Factory Method | In Java, for example, replace using the new operator and constructor call with invoking a helper method that creates the object (hiding the details). |
This example demonstrates the common Extract Method refactoring. Notice in the 358), but the principle is more easily relaxed for
private methods. It is a guideline, not a rule.
public class Player
{
private Piece piece;
private Board board;
private Die[] dice;
// …
public void takeTurn()
{
// roll dice
int rollTotal = 0;
for (int i = 0; i < dice.length; i++)
{
dice[i].roll();
rollTotal += dice[i].getFaceValue();
}
Square newLoc = board.getSquare(piece.getLocation(), rollTotal);
piece.setLocation(newLoc);
}
} // end of class
Now here's the code after applying the Extract Method refactoring:
public class Player
{
private Piece piece;
private Board board;
private Die[] dice;
// …
public void takeTurn()
{
// the refactored helper method
int rollTotal = rollDice();
Square newLoc = board.getSquare(piece.getLocation(), rollTotal);
piece.setLocation(newLoc);
}
private int rollDice()
{
int rollTotal = 0;
for (int i = 0; i < dice.length; i++)
{
dice[i].roll();
rollTotal += dice[i].getFaceValue();
}
return rollTotal;
}
} // end of class
We will see in iteration-2 that this
rollDice helper method is not a great solutionthe Pure Fabrication pattern will suggest an alternative that also preserves the Command-Query Separation Principlebut it suffices to illustrate the refactoring operation.
As a second short example, one of my favorite simple refactorings is
Introduce Explaining Variable because it clarifies, simplifies, and reduces the need for comments. The listings in Figure 21.4 and Figure 21.5 illustrate.
// good method name, but the logic of the body is not clear
boolean isLeapYear( int year )
{
return( ( ( year % 400 ) == 0 ) ||
( ( ( year % 4 ) == 0 ) && ( ( year % 100 ) != 0 ) ) );
}
// that's better!
boolean isLeapYear( int year )
{
boolean isFourthYear = ( ( year % 4 ) == 0 );
boolean isHundrethYear = ( ( year % 100 ) == 0);
boolean is4HundrethYear = ( ( year % 400 ) == 0);
return (
is4HundrethYear
|| ( isFourthYear && ! isHundrethYear ) );
}
Most of the dominant IDEs include automated refactoring support. See Figure 21.6 and Figure 21.7 for an example in the Eclipse IDE of applying the Extract Method refactoring. The
rollDice method is automatically generated, as is the call to it from the
takeTurn method. Notice that the tool is smart enough to see the need to return the
rollTotal variable. Nice!