Applying UML and Patterns: An Introduction to ObjectOriented Analysis and Design and Iterative Development, Third Edition [Electronic resources]

Craig Larman

نسخه متنی -صفحه : 472/ 270
نمايش فراداده

21.2. Refactoring

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).

Example

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.

Figure 21.2. The takeTurn method before refactoring.

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:

Figure 21.3. The code after refactoring with Extract Method.

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.

Figure 21.4. Before introducing an explaining variable.

// 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 ) ) );

}

Figure 21.5. After introducing an explaining variable.

// 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 ) );

}

IDE Support for Refactoring

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!

Figure 21.6. IDE before refactoring.

[View full size image]

Figure 21.7. IDE after refactoring.