You are on page 1of 185

Refactoring

Introduction Outline

A.
B.
C.
D.

What is Refactoring?
Why do we Refactor?
How do we Refactor?
When do we Refactor?

Refactoring

Refactoring

Refactoring
A.

What is Refactoring?

Refactoring is a technique which identifies


bad code and allows promotes the restructuring of that bad code into classes and
methods that are more readable,
maintainable, and generally sparse in code.
Refactoring yields a better design of both
your classes and methods.

Refactoring
C.

How do we Refactor?

Refactoring is accomplished by
1)
2)
3)
4)

building, decomposing, and moving methods


building and decomposing classes
replacing code with patterns, and
replacing code with other techniques.

Refactoring
D. When do we Refactor?
Refactoring is done when
1)
2)
3)
4)
5)

methods or classes are too LARGE,


code, control flow, or data structures are DUPLICATED,
attributes or methods are MISPLACED,
When parameters can make code reusable
When design is poor (the code smells).

Refactoring
Benefits of Refactoring
Without refactoring, the design of the program
decays.
Refactoring helps you to develop code more
quickly.
Refactoring helps you to find bugs.
Refactoring makes software easier to
understand.

Refactoring
An example- Class Diagram
Movie
pricecode: int
children = 2
regular = 0
new_release=1
title: String
getPriceCode)_
setPriceCode()
getTitle()

Rental

Customer
name: String
rentals: vector

daysRented:int

0..*

1..1

1..1

0..*
getMovie()
getDaysRented)_

Statement()
addRental(rental)
getName():

Refactoring
An Example Sequence Diagram

Customer

Rental

Movie

statement
forallrentals
Rental: getMovie()
getPriceCode()
getDaysRented()

Refactoring
Movie
pricecode: int
children = 2
regular = 0
new_release=1
title: String

public class Movie {


public static final int CHILDRENS = 2; // type movie for children
getPriceCode)_
public static final int REGULAR = 0; // type of regular movie setPriceCode()
public static final int NEW_RELEASE = 1; // new release moviegetTitle()
private String _title; // title of the movie
private int _priceCode; // price code for movie
public Movie(String title, int priceCode) {
_title = title;
_priceCode = price Code;
Constructor
}// end constructor
public int getPriceCode() { return priceCode; }
public void setPriceCode (int agr) { _priceCode = arg; }
public String getTitle () { return _Title; }
} // end Movie

Refactoring

Rental
daysRented:int

getMovie()
getDaysRented)_

class Rental {
private Movie _movie; // movie rented
private int _daysRented; // number of days rental
public Rental(Movie movie, int daysRented) {
_movie = movie;
_daysREnted = daysRented;
Constructor
} // end Rental
public int getDaysRented() { return _daysRented; }
public Movie getMovie() { return _movie; }
}// end Rental

Refactoring

Customer
name: String
rentals: vector

Statement()
addRental(rental)
getName():

class Customer {
private String _name; // name of customer
private Vector _rentals = new Vector (); // vector of list of
rentals by the customer

Constructor

public Customer (String name) { _name = name; }


public void addRental (Rental arg)
{ _rentals.addElement(arg) }
public String getName () { return _name}

Refactoring
public String statement() {
double totalAmount = 0; // total of the statement
int frequentRenterPoints = 0; // number of frequent
rental points of a rental
Enumeration rentals = _rentals.elements(); // list of
rentals
String result = Rental Record for + getName()
+/n
while (rentals.hasMoreElements() {
double thisAmount =0;
Rental each = (Rental) rentals.nextElement();
// continued on next page

Refactoring
// determine amounts for each line // regular 2.00 for two days 1.50
extra
// new release 3.00 per day
// children 1.50 for three days
switch (each.getMovie().getPriceCode()) {
case Movie.REGULAR: // statements for a regular movie
thisAmount +=2;
if (each.getDaysRented() >2)
thisAmount +=(each.getDaysRented()-2)*1.5;
Break;
case Movie.NEW_RELEASE: // statements for a new release type
movie
thisAmount +=each.getDaysRented()*3;
Break;
case Movie_CHILDREN: // statements for a children movie
thisAmount +=1.5;
if (each.getDaysRented() >3) thisAmount
+=(each.getDaysRented()-3)*1.5;

Refactoring

// add frequent renter points


add 1 frequent renter point if NEW RELEASE rented > one day
frequentRenterPoints ++;
// add bonus for a two day new release rental
if ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) &&
each.getDaysRented() > 1) frequentRenterPoints ++;
// show figures for this rental
result +=/t + each.getMovie().getTitle*(+\t +
String.valueOf(thisAmount) + \n;
totalAmount +=thisAmount;
}
// add footer lines
result +=Amount owed is + String.valueOf(totalAmount) +
\n;
result += You earned + String.valueOf(frequentRenterPoints) +
frequent renter points;
return result;
} // end statement
// end customer

Refactoring

Refactoring Opportunities

a code cluster is setting one variable


EXTRACT it as a METHOD
returning the variable
// determine amounts for each line
switch (each.getMovie().getPriceCode()) {
case Movie.REGULAR:
thisAmount +=2;
if (each.getDaysRented() >2) thisAmount
+=(each.getDaysRented()-2)*1.5;
Break;
case Movie.NEW_RELEASE:
thisAmount +=each.getDaysRented()*3;
Break;
case Movie_CHILDREN:
thisAmount +=1.5;
if (each.getDaysRented() >3) thisAmount
+=(each.getDaysRented()-3)*1.5;
Break;
} // end switch

Refactoring
EXTRACT METHOD
private double amountFor(Rental each)
double thisAmount = 0.0;
switch (each.getMovie().getPriceCode()) {
case Movie.REGULAR:
thisAmount +=2;
if (each.getDaysRented() >2) thisAmount
+=(each.getDaysRented()-2)*1.5;
Break;
case Movie.NEW_RELEASE:
thisAmount +=each.getDaysRented()*3;
Break;
case Movie_CHILDREN:
thisAmount +=1.5;
if (each.getDaysRented() >3) thisAmount
+=(each.getDaysRented()-3)*1.5;
Break;
} // end switch
return thisAmount;
} // end amountFor

page 11

Refactoring
Original Code - Customer Class

page 18

public String statement() {


double totalAmount = 0;
int frequentRenterPoints = 0;
Enumeration rentals = _rentals.elements();
String result = Rental Record for + getName()
+/n
while (rentals.hasMoreElements() {
double thisAmount =0;
Rental each = (Rental) rentals.nextElement();

Calls the new method

thisAmount = amountFor(each);

Refactoring
Refactoring Opportunities
a variable resides in the wrong class
MOVE the METHOD
to the class where it should reside
public String statement() {
double totalAmount = 0;
int frequentRenterPoints = 0;
Enumeration rentals = _rentals.elements();
String result = Rental Record for + getName()
+/n
while (rentals.hasMoreElements() {
double thisAmount =0;
Rental each = (Rental) rentals.nextElement();
Should NOT be in customer class

Should be in the rental


thisAmount = amountFor(each);

Refactoring

MOVE METHOD

and rename

private double getCharge(Rental each)


double result = 0.0;
switch (each.getMovie().getPriceCode())
{
Rename

the method and result

case Movie.REGULAR:
result +=2;
if (each.getDaysRented() >2) result +=(each.getDaysRented()2)*1.5;
Break;
case Movie.NEW_RELEASE:
result +=each.getDaysRented()*3;
Break;
case Movie_CHILDREN:
result +=1.5;
if (each.getDaysRented() >3) result +=(each.getDaysRented()3)*1.5;
Break;
} // end switch
return result;

Refactoring
Original Code - Customer Class
public String statement() {
double totalAmount = 0;
int frequentRenterPoints = 0;
Enumeration rentals = _rentals.elements();
String result = Rental Record for + getName()
+/n
while (rentals.hasMoreElements() {
double thisAmount =0;
Calls the
Rental each = (Rental) rentals.nextElement();

page 19

new method
In the rental class

thisAmount = each.getCharge(each);

Refactoring
Refactoring Opportunities
a variable is used temporarily
REPLACE TEMP with QUERY
public String statement() {
eliminating the temp
int frequentRenterPoints = 0;
Enumeration rentals = _rentals.elements();
String result = Rental Record for + getName() +/n
while (rentals.hasMoreElements() {
Rental each = (Rental) rentals.nextElement();
frequentRenterPoints += getFrequentRenterPoints();
// show figures for this rental
result +=/t + each.getMovie().getTitle*(+\t +
String.valueOf(each.getCharge()) + \n;
} // end loop
// add footer lines
result +=Amount owed is + String.valueOf(getTotalCharge) + \n;
result += You earned + String.valueOf(frequentRenterPoints) + frequent renter
points;
return result;
} // end statement

Problem temp in loop

Refactoring
EXTRACT it as a METHOD
private double getTotalFrequentRentalPoints() {
int result = 0;
Enumeration rentals = _rentals.elements();
while (rentals.hasMoreElements() {
Rental each = (Rental) rentals.nextElement();
result += each.getFrequentRentalPoints();
} // end loop
return result;
} // end getTotalFrequentRentalPoints

Refactoring
Refactoring Opportunities
conditional code exist for sub-types
Use POLYMORPHISM
replacing conditional logic
private double getCharge(Rental each)
double result = 0.0;
switch (getMovie().getPriceCode()) {
case Movie.REGULAR:
result +=2;
if (getDaysRented() >2) result +=(getDaysRented()-2)*1.5;
Break;
case Movie.NEW_RELEASE:
result +=getDaysRented()*3;
Break;
case Movie_CHILDREN:
result +=1.5;
if (getDaysRented() >3) result +=(getDaysRented()-3)*1.5;
Break;
} // end switch
return result;
} // end getCharge

Cannot make subclasses of movie


Movies can change classifications

Refactoring
Refactoring Opportunities
object can change states in its lifetime
Use the STATE Pattern
for the variables which change values
private double getCharge(Rental each)
double result = 0.0;
switch (getMovie().getPriceCode()) {
case Movie.REGULAR:
result +=2;
if (getDaysRented() >2) result +=(getDaysRented()-2)*1.5;
Break;
case Movie.NEW_RELEASE:
result +=getDaysRented()*3;
Break;
case Movie_CHILDREN:
result +=1.5;
if (getDaysRented() >3) result +=(getDaysRented()-3)*1.5;
Break;
} // end switch
return result;
} // end getCharge

Cannot make subclasses of movie


Movies can change classifications

Refactoring
State Pattern- Class Diagram
Price
Movie

1..1
1..1

getCharge()

pricecode: int
children = 2
regular = 0
new_release=1
title: String
getPriceCode)_
setPriceCode()
getTitle()
getCharge()

Regular Price

getCharge()

Childrens Price

getCharge()

New Release Price

getCharge()

Refactoring
Refactoring Opportunities
state variables not encapsulated
SELF ENCAPSULATE FIELDS
add get and set methods
private double getCharge(int daysRented)
double result = 0.0;
switch (getMovie().getPriceCode()) {
case Movie.REGULAR:
result +=2;
if (getDaysRented() >2) result +=(getDaysRented()-2)*1.5;
Break;
case Movie.NEW_RELEASE:
result +=getDaysRented()*3;
Break;
case Movie_CHILDREN:
result +=1.5;
if (getDaysRented() >3) result +=(getDaysRented()-3)*1.5;
Break;
} // end switch
return result;
} // end getCharge

Cannot make subclasses of movie


Movies can change classifications

Refactoring
Original Code --

page 3

public class Movie {


public static final int CHILDRENS = 2;
public static final int REGULAR = 0;
public static final int NEW_RELEASE = 1;
private String _title;
private int _priceCode;

} Sub-types
Self Encapsulate

public Movie(String title, int priceCode) {


_title = title;
_priceCode = price Code;
}// end constructor
public int getPriceCode() { return priceCode; }
public void setPriceCode (int agr) { _priceCode = arg; }
public String getTitle () { return _Title; }
} // end Movie

Refactoring
public class Movie {
public static final int CHILDRENS = 2;
public static final int REGULAR = 0;
public static final int NEW_RELEASE = 1;
private String _title;
private int _priceCode;

setPriceCode(priceCode);

public Movie(String title, int priceCode) {


_title = title;
_priceCode = price Code;
}// end constructor
public int getPriceCode() { return priceCode; }
public void setPriceCode (int agr) { _priceCode = arg; }
public String getTitle () { return _Title; }
} // end Movie

Refactoring
Movie Price Sub-Classes -class Price {
abstract int getPriceCode();
} // end Price
class ChildrensPrice extends Price {
int getPriceCode () { return Movie.CHILDRENS; }
} // end ChildrensPrice
class NewReleasePrice extends Price {
int getPriceCode () { return Movie.NEW_RELEASE; }
} // end NewReleasePrice
class RegularPrice extends Price {
int getPriceCode () { return Movie.REGULAR; }
} // end RegularPrice

page 41

Refactoring
MOVE METHOD

page 45

class Price
private double getCharge(int daysRented)
double result = 0.0;
switch (getMovie().getPriceCode()) {
case Movie.REGULAR:
result +=2;
if (getDaysRented() >2) result +=(getDaysRented()-2)*1.5;
Break;
case Movie.NEW_RELEASE:
result +=getDaysRented()*3;
Break;
case Movie_CHILDREN:
result +=1.5;
if (getDaysRented() >3) result +=(getDaysRented()-3)*1.5;
Break;
} // end switch
return result;
} // end getCharge
}// end price

Once it is moved, we can replace


conditional with polymorphism

Refactoring
Use POLYMORPHISM

page 47

class RegularPrice
double getCharge(int daysRented)
double result = 2;
if (getDaysRented() >2) result +=(getDaysRented()-2)*1.5;
return result;
} // end getCharge
} // end regularPrice
class ChildrensPrice
double getCharge (int daysRented) {
double result = 1.5;
if (getDaysRented() >3) result +=(getDaysRented()-3)*1.5;
return result
} // end getCharge
} // end ChildrensPrice
class NewRelease
double getCharge (int daysRented() { return daysRented * 3; }

Take one leg of case statement at a time.

Refactoring
Use POLYMORPHISM

page 47

class Price
abstract double getCharge (int daysRented);

Create an overiding method for the getCharge method.

Refactoring
We will look at several types of refactoring.
These include the refactoring of:
methods

generalization

classes

data

calls

conditional expressions

And some other BIG refactoring.

Refactoring
Refactoring and Composing Methods
Extract Methods from code
Inline Methods to code
Replace Temp with Query
Introduce Explaining Variables
Split Temporary Variables
Remove Assignments to Parameters
Replace Method with Method Objects
Substitute Algorithm

Refactoring
Refactoring by
Moving Features Between Objects
Move Method
Move Field
Extract Class
Inline Class
Hide Delegate
Remove Middle Man
Introduce Foreign Method
Introduce Local Extension

Refactoring
Refactoring by Organizing Data
Self Encapsulate Field

Encapsulate Field

Replace Data Value with Object

Encapsulate Collection

Change Value to Reference

Replace Record with Data Class

Change Reference to Value

Replace Type with Data Class

Replace Array with Object

Replace Type Code with Subclasss

Duplicate Observed Data


State/Strategy

Replace Type Code with

Change Unidirectional Direction to Bidirectional

Replace Subclass

Change Bidirectional Direction to Unidirectional

with Field

Replace Magic Number with Symbolic Constant

Refactoring
Refactoring by Simplifying Conditional Expressions
Decompose Conditional
Consolidate Conditional Expression
Consolidate Duplicate Conditional Fragments
Remove Control Flag
Replace nested Conditional with Guard Clauses
Replace Conditional with Polymorphism
Introduce Null Object
Introduce Assertion

Refactoring
Refactoring by Making Method Calls Simpler
Rename Method

Introduce Parameter Object

Add Parameter

Remove Setting Method

Remove Parameter

Hide Method

Separate Query from Modifier

Replace Constructor with Factory

Parameterize Method

Encapsulate Downcast

Replace Parameter with Explicit Methods


Preserve Whole Object

Replace Error Code with Exception

Replace Parameter with Method Replace Exception with Test

Refactoring
Refactoring by Dealing with Generalization
Pull Up Field

Extract Interface

Pull Up Method

Collapse Hierarchy

Pull Up Constructor Body

Form Template Method

Push Down Method

Replace Inheritance with

Push Down Field

Delegation

Extract Subclass

Replace Delegation with

Extract Superclass

Inheritance

Refactoring
Refactoring with Big Refactoring
Tease Apart Inheritance
Convert Procedural Design to Objects
Separate Domain from Presentation
Extract Hierarchy

Refactoring
Lesson One:
Self-Documenting Code and
Functional Testing

Refactoring
Process
1. Read, review and understand
2. Format and comment
3. Perform documenting refactorings

Refactoring
1. Read, review and understand
A. Read existing code
Not part of refactoring but necessary.
B. Review it for understandability
Do the variables have meaningful names?
Are their enough comments
Do the methods have meaningful names?

Refactoring
2. Format and comment

A. Format the code according to supplied standards


Format the code by some standard to aid in readability
Comment code for understanding
After reading the code add any needed comments to
increase understandability

Refactoring
4. Perform Refactorings
1. Rename Method
2. Rename Constants
3. Rename Variables

Refactoring
Rename Method
Summary:
The name of a method does not reveal its purpose

Change the name of the method.

Refactoring
Rename Method:
Motivation:
Methods should be named in a way the
communicates their intension. Think what the
comment for the method would be and turn that
comment into the name of the method.

Refactoring
Rename Method:

Customer
____________

Customer
____________

getinvcrelmt ()

getInvoiceCreditLimit()

Refactoring
Rename Method:
Mechanics:
Check if method signature is implemented by a super or sub class. If so
perform these steps for each implementation.
Declare new method with new name and copy old body into new
method.
Change body of old so it calls the new one.
Find references to old and change them to refer to new one.
Remove old method.
Compile and test.

Refactoring
Rename constants, variables methods
CONSTANTS
static final int CONTINUE = 0; // OLD OK status for game continues
static final int ENDINGSTATE=(1<< 9)-1; //OLD DONE 111 111 111
Change these two constants to make code clearer
TEST BETWEEN CHANGES.

Refactoring

if (won[black])
{
if ((black | white) == DONE) {
return OK;
BECOMES
if (won[black])
{
if ((black | white) == DONE) {
return CONTINUE;
TEST BETWEEN CHANGES.

return LOSE;
}
return STALEMATE;

return LOSE;
}
return STALEMATE;

Refactoring
VARIABLES

Rename constants, variables methods

Image crossImage; // user image


Image notImage; // computer image
BECOMES
computerImage = getImage(getCodeBase(), "oimage.gif");
userImage = getImage(getCodeBase(), "ximage.gif");

int white; // White's current position. The computer is white.


int black; Black's current position. The user is black.
BECOMES
int userStatus; //user bitmask denotes user squares occupied OLD BLACK
int computerStatus; //computer bitmask denotes squares occupied WHITE

Refactoring
if ((white & (1 << i)) != 0) {
g.drawImage(notImage, c*xoff + 1, r*yoff + 1, this);
} else if ((black & (1 << i)) != 0) {
g.drawImage(crossImage, c*xoff + 1, r*yoff + 1, this);
BECOMES
if ((computerStatus & (1 << i)) != 0) { // if computer square taken
g.drawImage(computerImage, c*xoff + 1, r*yoff + 1, this);
} else if ((userStatus & (1 << i)) != 0) { // if user square taken
g.drawImage(userImage, c*xoff + 1, r*yoff + 1, this);

Refactoring
VARIABLES
int bestMove(int white, int black)
BECOMES
int bestMove(int computerStatus,int userStatus) { //compute best move
int pw = white | (1 << mw);
BECOMES
int pw = computerStatus | (1 << mw);
int pb = black | (1 << mb);
BECOMES
int pb = userStatus | (1 << mb);

Refactoring
white = black = 0;
BECOMES
computerStatus = userStatus = 0;
white |= 1 << (int)(Math.random() * 9);
BECOMES
computerStatus |= 1 << (int)(Math.random() * 9);
white |= 1 << best;
BECOMES
computerStatus |= 1 << best;

Refactoring
Rename constants, variables methods
MORE VARIABLES
static int moves[] = {4,0,2,6,8,1,3,5,7};
BECOMES
static int mostStrategicMove[] = {4,0,2,6,8,1,3,5,7};
int mw = moves[i];
BECOMES
int mw = mostStrategicMove[i];

Refactoring
Rename constants, variables methods
METHODS
boolean myMove(int m) {
boolean yourMove() {
BECOMES
boolean legalComputerMove() {
boolean legalUserMove(int m) {
if (myMove())
if (yourMove(c + r * 3))
BECOMES
if (legalComputerMove())
if (legalUserMove(c + r * 3))

Refactoring
Rename constants, variables methods

OTHERS
mw
BECOMES
int potentialComputerMove = mostStrategicMove[i];
pb
BECOMES
int potentialUserStatus = userStatus | (1 << j);
c and r BECOMES row and col

Refactoring
A big difference

BEFORE

loop:
for (int i = 0 ; i < 9 ; i++) {
int mw = moves[i];
if (((white & (1 << mw)) == 0) && ((black & (1 << mw)) == 0)) {
int pw = white | (1 << mw);
if (won[pw]) {return mw; } // white wins, take it!
for (int mb = 0 ; mb < 9 ; mb++) {
if (((pw & (1 << mb)) == 0) && ((black & (1 << mb)) == 0)) {
int pb = black | (1 << mb);
if (won[pb]) {continue loop; } // black wins, take another
}
}
}

Refactoring
A big difference

AFTER

loop:
for (int i = 0 ; i < 9 ; i++) { // for square = 0 to < 9
int potentialComputerMove = mostStrategicMove[i];
if (((computerStatus & (1 << potentialComputerMove)) == 0) &&
((userStatus & (1 << potentialComputerMove)) == 0)) {
int potentialComputerStatus =
computerStatus | (1 << potentialComputerMove);
if (winningState[potentialComputerStatus]) { // computer wins
return potentialComputerMove;
} /// end if (not user taken ) && ( not computer taken )

Refactoring
A big difference
// the computer did not find a winning move
for (int j = 0 ; j < 9 ; j++) { // for square = 0 to < 9
if (((potentialComputerStatus & (1 << j)) == 0) &&
((userStatus & (1 << j)) == 0)) {
int potentialUserStatus = userStatus | (1 << j);
if (winningState[potentialUserStatus]) { // user wins, take another
continue loop;
} // end if won
} // end if &&
} // end for
// found a move but user would win

Refactoring
A big difference
// computer has not found a winning move
if (bestmove == -1) { // neither can win, this move will do
bestmove = potentialComputerMove;
} // end if
} // end if &&
} // end for
if (bestmove != -1) { // if no move found return the best one
return bestmove;
} // end if bestmove

Refactoring
A big difference
// there is no winning or good move take mostStrategic move
for (int i = 0 ; i < 9 ; i++) { // no great move, try first one open
int firstAvailableComputerMove = mostStrategicMove[i];
if (((computerStatus&(1<< firstAvailableComputerMove))== 0)
&& (userStatus & (1 << firstAvailableComputerMove)) == 0)) {
return firstAvailableComputerMove;
} // end if &&
} // end for
return -1; // return no more moves
} // end best move

Refactoring
Encapsulate

Refactoring
Process
1. Encapsulate ALL class variables
2. Unit test
3. Functionally test

Refactoring
1. Encapsulate ALL class variables
A.

Write get and set methods for ALL class variables


Modify ALL references to class variables to use get
and set methods.
Test methods.

Refactoring
Write getters and setters for all class variables
public int getComputerStatus () { return computerStatus; }
public Image getUserImage (){return userImage;}
public boolean getFirst () { return first; }
public Image getComputerImage () { return computerImage;}
public void setComputerStatus (int computerStatus)
{ this.computerStatus = computerStatus; }
public void setUserImage (Image userImage)
{ this.userImage = userImage;}
public void setFirst (boolean first) { this.first = first; }
public void setCommputerImage (Image computerImage)
{ this.computerImage = computerImage; }

Refactoring
Perform other encapsulation
refactorings
1. Self encapsulating field
2. Encapsulate field
3. Encapsulate collection
These are not covered in detail

Refactoring
// GETS AND SETS

ADDED TO CODE

public int getComputerStatus () { return computerStatus; }


public Image getUserImage (){return userImage;}
public boolean getFirst () { return first; }
public Image getComputerImage () { return computerImage;}

public void setComputerStatus (int computerStatus)


{ this.computerStatus = computerStatus; }
public void setUserImage (Image userImage)
{ this.userImage = userImage;}
public void setFirst (boolean first) { this.first = first; }
public void setCommputerImage (Image computerImage)
{ this.computerImage = computerImage; }

Refactoring
//METHODS that need changes in their access to variables
int bestMove(int computerStatus, int userStatus) {
has parameters making them local variables scoping? ok
boolean legalUserMove(int canidateMove) {
boolean legalComputerMove() {
int gameStatus(int computerStatus, int userStatus) {
public void paint(Graphics g) { // paint the screen
public void mouseReleased(MouseEvent e) {

Refactoring
//METHODS that need changes in their access to variables
computerStatus = userStatus = 0;
BECOMES
setComputerStatus(0);
setUserStatus(0);
if ((userStatus | computerStatus) == ENDINGSTATE) {
BECOMES
if ((getUserStatus() | getComputerStatus()) == ENDINGSTATE) {
computerImage = getImage(getCodeBase(), "oimage.gif");
BECOMES
setComputerImage(getImage(getCodeBase(), "oimage.gif"));

Refactoring
// domain functionality and GUI
switch (gameStatus()) { // determine status
case WIN:
case LOSE:
case STALEMATE:
play(getCodeBase(), "audio/return.au");
computerStatus = userStatus = 0;
if (first) { // reset first
computerStatus |= 1 << (int)(Math.random() * 9);
}// end if
first = !first;
repaint(); // GUI controlling when to display
// RED LINED code NEEDS TO BE A METHOD TO TEST

Refactoring
Make it a METHOD
public void resetFirst() {
if (getComputerFirst()) { // reset who is first
setComputerStatus ( 1 << (int)(Math.random() * 9));
}// end if
setComputerFirst (!getComputerFirst());
} // end resetStatus

Refactoring
Call the METHOD Now this is all GUI code
switch (gameStatus()) { // determine status
case WIN:
case LOSE:
case STALEMATE:
play(getCodeBase(), "audio/return.au");
resetStatus();
repaint();
return;
} // end switch

Refactoring

Constants and Variables

Refactoring
Process
1. Review scope of all constants
2. Review scope of all variables
3. Adjust any gets/sets due to reviews
4. Apply refactorings

Refactoring
1. Review the scope of all constants
2. Review the scope of all variables
3. Adjust code to limit scope
A.

Review all the constants and variables to make sure


they are not MORE global in scope than necessary.
Downcast any that are too broad in scope.
Adjust gets and sets to adapt to new or modified
scope.

Refactoring
3. Adjust code to limit scope

2
1
3
Investigate variables, parameters, and methods to see if they can be local
or need to be uplifted to later evaluate their partitioning into classes.

Refactoring
3. Adjust code to limit scope
private static int mostStrategicMove[] = {4,0,2,6,8,1,3,5,7};
// square order of importance
BECOMES

int mostStrategicMove[] = {4,0,2,6,8,1,3,5,7};// square order of importanc


// in the method bestMove

Refactoring
4. Apply Refactorings
Replace Magic Number with Symbolic
Constant
Introduce Explaining Variable
Split Temporary Variable
Replace Temp with Query
Separate Query from Modifier ****
Replace Array with Object *****
Not all of these are covered in detail.

Refactoring

Replace Magic Number with Symbolic Constant


Summary:
You have a literal number with a particular meaning

Create a constant, name it after the meaning, and


replace the number with it.

Refactoring
Replace Magic Number with
Symbolic Constant:
Motivation:
Magic numbers are numbers with special values that
usually are not obvious.

Add a constant to store the values of these magic


numbers.

Refactoring
Replace Magic Number with
Symbolic Constant:
Example:
double potentialEnergy (double mass, double height) {
return mass * 9.81 * height;
}// end potential Energy

Should be
double potentialEnergy (double mass, double height {
return mass * GRAVITATIONAL_CONSTANT * height;
}// end potentialEnergy
static Final double GRAVITATIONAL_CONSTANT * 9.81;

Refactoring
Replace Magic Number with Symbolic
Constant:
Mechanics:
declare a constant and set to magic number
find occurrences of magic number
if magic matches constant usage use constant
when all magic numbers changed
compile and test.

Refactoring
Replace Magic Number with Symbolic Constant:
static final int ENDINGSTATE = (1 << 9) - 1;
BECOMES
static final int NINE_SHIFTING_BITS = 9;
static final int ENDINGSTATE = (1 << NINE_SHIFTING_BITS) - 1;
for (int i = 0 ; i < 9 ; i++) { // for square = 0 to < 9
BECOMES
static final int firstCell = 0; // first cell at row 1 col 1
static final int lastCell = 8; // last cell at row 3, col 3
for (int i = firstCell ; i <= lastCell ; i++) { \

Refactoring
Replace Magic Number with Symbolic Constant:
int bestmove = -1;
if (bestmove == -1) {
if (bestmove != -1) {
return -1; // return no more moves
BECOMES
static final int bestMoveNotFound = -1; //indicating best move not
found
int bestmove = bestmoveNotFound;
if (bestmove == bestMoveNotFound) {
if (bestmove != bestMoveNotFound) {
return bestMoveNotFound; // return no more moves

Refactoring
Replace Magic Number with Symbolic Constant:
for (int row = 0 ; row < 3 ; row++) {
for (int col = 0 ; col < 3 ; col++, i++) {
int col = (x * 3) / d.width;// determine col
int row = (y * 3) / d.height; // determine the row
BECOMES
static final int NUMBER_OF_COLUMNS = 3;
static final int NUMBER_OF_ROWS = 3;
for (int row = 0 ; row < NUMBER_OF_ROWS ; row++) {
for (int col = 0 ; col < NUMBER_OF_COLUMNS ; col++, i++) {
int col = (x * NUMBER_OF_COLUMNS) / d.width;// determine col
int row = (y * NUMBER_OF_ROWS) / d.height; // determine the row

Refactoring

4. Introduce Explaining Variable


Summary:
You have a complicated expression.
Put the result of the expression in a temp with a name
that explains the purpose.

Refactoring
4. Introduce Explaining Variable:
Motivation:
Complex code that requires many lines of code but
could more easily be explained with some variable
name that helps to explain the code semantics.

Refactoring
4. Introduce Explaining Variable:

Example:

If ( (platform.toUpperCase().indexOf(MAC) > -1) &&


(browser.toUpperCase().indexOf(IE) > -1) &&
wasInitialized () && resize >0)
{ .. other code ..}
goes to
final boolean isMacOs
> -1;

= platform.toUpperCase().indexOf(MAC)

final boolean isIEBrowser


-1;

= platform.toUpperCase().indexOf(IE) >

final boolean wasResized

= resize >0;

Refactoring
4. Introduce Explaining Variable:
Mechanics:
declare a final temp variable
set it to the result portion of the expression
compile and test

Refactoring

5. Split Temporary Variables


Summary:
You have one temp assigned to more than once and it
is not a loop variable.
Make a new temp for each assignment

Refactoring
5. Split Temporary Variables:
Motivation:
Looping variables (control variables of a loop) or
accumulator variables (summing variables) may have
the need for assignments more than once. Other
variables should not be assigned to more than once.
Investigation of greater than one assignments to the
same variable may yield variables that are actually
used for different semantics.

Refactoring
5. Split Temporary Variables:

Example:

double getDistanceTravelled (int time) {


double result;
double acc = _primaryForce / _mass;
force

// initial value of acceleration of first

int primaryTime = Math.min(time, _delay);


result = 0.5 * acc * primaryTime * primaryTime;
int secondaryTime = time - _delay;
if (secondaryTime > 0) {
double primaryVel = acc * _delay;
acc = (_primaryForce * _secondaryForce) / _mass; // final value of acceleration
result += primaryVel * secondaryTime + 0.5 *acc * secondaryTime *
secondaryTime;
} // end if
return result;

Refactoring
5. Split Temporary Variables:

Example:

double getDistanceTravelled (int time) {


double result;
double primaryacc = _primaryForce / _mass; // initial value of acceleration of first force
int primaryTime = Math.min(time, _delay);
result = 0.5 * primaryacc * primaryTime * primaryTime;
int secondaryTime = time - _delay;
if (secondaryTime > 0) {
double primaryVel = acc * _delay;
double acc = (_primaryForce * _secondaryForce) / _mass; // final value of acceleration
result += primaryVel * secondaryTime + 0.5 *acc * secondaryTime * secondaryTime;
} // end if
return result;
} // end getDistanceTravelled
CAN YOU THINK OF OTHER REFACTORING?

Refactoring
5. Split Temporary Variables:
Mechanics:
find the variables assigned to more than once
change the name for each different assignments
change references
compile and test

Refactoring

3. Replace Temp with Query


Summary:
You are using a temporary variable to hold the result
of an expression.
Extract the expression into a method Replace all
references to the temp with the expression.

Refactoring
3. Replace Temp with Query:
Motivation:
Methods with many temps tend to be long.
Replacing the temp variable with a query method
makes the code cleaner for further refactoring.

Refactoring
3. Replace Temp with Query:
double getPrice(){
int basePrice = _quanity * itemPrice;
double discountFactor;
if (basePrice > 1000) discountFactor = 0.95
else discountFactor = 0.98;
return basePrice *discountFactor;
}// end getPrice

Example:

Refactoring
3. Replace Temp with Query:

Example:

double getPrice(){
int basePrice = _quanity * itemPrice;
double discountFactor;
if (basePrice() > 1000) discountFactor = 0.95
else discountFactor = 0.98;
return basePrice() *discountFactor;
}// end getPrice
private int basePrice () { return quanity * itemPrice; }

Refactoring
3. Replace Temp with Query:

Example:

double getPrice(){
double discountFactor = discountFactor();
if (basePrice() > 1000) discountFactor = 0.95
else discountFactor = 0.98;
return basePrice() *discountFactor;
}// end getPrice
private double discountFactor () {
if (basePrice() > 1000) return 0.95;
else return 0.98;
} // end discountFactor

Extract Method

Refactoring
3. Replace Temp with Query:

Example:

double getPrice(){
double discountFactor = discountFactor();
return basePrice() *discountFactor;
}// end getPrice
private double discountFactor () {

Can I do more?
Replace Temp with Query

if (basePrice() > 1000) return 0.95;


else return 0.98;
} // end discountFactor
private int basePrice () { return quanity * itemPrice; }

Refactoring
3. Replace Temp with Query:
Mechanics:
declare the temp as final to check references
compile to test
extract the right hand side as a method body
if a loop take entire loop
name and construct the method
replace the temp with the call

Refactoring
3. Replace Temp with Query:
int best = bestMove(computerStatus, userStatus);
setComputerStatus (computerStatus | 1 << best);
BECOMES
setComputerStatus
(computerStatus | 1 << bestMove(computerStatus, userStatus));

Refactoring

Methods

Refactoring
Learning objective
good object-oriented methods.
Primary refactoring used to produce
good methods is Extract Method,
but others are covered.

Refactoring
Good method is defined as a
method with an acceptable level of

cohesion [Kwang99,Smith04]

Refactoring
Good method is further defined
as a method
acceptably cohesive
less than 20 lines in length
accomplishes one functionality
testable with one unit test method

Refactoring
Kang [KAN99, SMI04] defines levels
of cohesion which are ranked from
the best level to the worst.
Functional, sequential and
communicational cohesion in
methods is an acceptable level.

Refactoring
Functional cohesion deals with the
ability of a method to produce only
one output (i.e., to change the state of
only one member variable) and is
considered the best level of cohesion.

Refactoring
In sequential cohesion, more than
one variable is modified; however, all
modifications result in the change to
only one member variable. The
outputs of one or more modifications
are the inputs to the modification of
the member variable.

Refactoring
In communicational cohesion,
multiple outputs are dependent on a
common input but are not derived in
a loop or selection statement. Not as
good as functional or sequential,
refactor if possible.

Refactoring
Method sizes may be very small as a
natural consequence of good objectoriented design [Lieberherr89]. Studies
have shown that larger methods result in a
reduction in understandability, reuse, and
maintainability. [Hudli94, Lorenz94,
McCabe94, Tegaden92].

Refactoring
Refactorings used
Extract Method
Inline Method

Refactoring
1. Extract Method
Summary: - most key refactoring method
You have a cluster of code which can be grouped into
a method to simplify the code.
Turn the cluster in to a method with a name that
explains the function of the method.

Refactoring
1. Extract Method:
Motivation:
Method is too long, needs comments to understand.
Need very small methods returning one value.
Method names are allowed to be longer than the code
itself.

Refactoring
1. Extract Method:

Example: no local variables

void printOwing() {
Enumeration e = _orders.elements();
double outstanding = 0.0;
// print banner
System.our.println (*************);
System.our.println (Customer Owes)
System.our.println (*************);

// caculate outstanding
while (e.hasMoreElements()) {
Order each = (Order) e.nextElement();
outstanding += each.getAmount();
// print details
System.out.println (name: + _name);
System.out.println (amount + outstanding);

Refactoring
1. Extract Method:

Example: no local variables

void printOwing() {
Enumeration e = _orders.elements();
double outstanding = 0.0;
printBanner();
// cacluate outstanding
while (e.hasMoreElements()) {
Order each = (Order) e.nextElement();
outstanding += each.getAmount();
// print details
System.out.println (name: + _name);
System.out.println (amount + outstanding);

Void printBanner() {
System.our.println (*************);
System.our.println (Customer Owes)
System.our.println (*************);
} // end printBanner

Refactoring
1. Extract Method:

Example: locals only referenced

void printOwing() {
Enumeration e = _orders.elements();
double outstanding = 0.0;
printBanner()
// cacluate outstanding
while (e.hasMoreElements()) {
Order each = (Order) e.nextElement();
outstanding += each.getAmount();
// print details
System.out.println (name: + _name);
System.out.println (amount + outstanding);

Void printBanner() {
System.our.println (*************);
System.our.println (Customer Owes)
System.our.println (*************);
} // end printBanner

Refactoring
1. Extract Method:

Example: locals only referenced


pass in needed data as parameters

void printOwing() {
Enumeration e = _orders.elements();
double outstanding = 0.0;
printBanner()
// cacluate outstanding
while (e.hasMoreElements()) {
Order each = (Order) e.nextElement();
outstanding += each.getAmount();
printDetails (outstanding);
.

Void printDetails (double outstanding) {


System.out.println (name: + _name);
System.out.println (amount + outstanding);

Refactoring
1. Extract Method:

Example: one local updated

void printOwing() {
Enumeration e = _orders.elements();
double outstanding = 0.0;
printBanner()
// cacluate outstanding
while (e.hasMoreElements()) {
Order each = (Order) e.nextElement();
outstanding += each.getAmount();

printDetails (outstanding);
Void printDetails (double outstanding) {
System.out.println (name: + _name);
System.out.println (amount + outstanding);

Refactoring
1. Extract Method:

Example: one local updated


return updated local

void printOwing() {
printBanner()
double outstanding = getOutstanding();
printDetails (outstanding);

Double getOutstanding() {
Enumeration e = _orders.elements();
double outstanding = 0.0;
// cacluate outstanding
while (e.hasMoreElements()) {
Order each = (Order) e.nextElement();
outstanding += each.getAmount();
} // end while
return outstanding;
} // end getOutstanding

Refactoring
1. Extract Method:

Mechanics

locate clusters of actual code


copy to a new method
if local variables needed define parameters
if local variable (one) updated make return type
build call
if local variable updated (more than one)
make multiple methods or split variables
build calls

Refactoring
2. Inline Method
Summary:
You have a method body that is just as clear as the
name of the method
Turn the method into inline code rather than a
method.

Refactoring
2. Inline Method:
Motivation:
The body is just as clear as a method name and there
exist needless indirection.

Refactoring
2. Inline Method:

Example:

int getRating ()}


return (moreThanFiveLateDeliveries () ? 2:1;
}// end getRating
boolean moreThanFiveLateDeliveries() {
return _numberofLateDeliveries > 5;
} // end moreThanFiveLateDeliveries

Refactoring
2. Inline Method:

Example:

int getRating ()}


return _numberofLateDeliveries > 5 ? 2:1;
} // end getRating

Refactoring
2. Inline Method:

Mechanics:

find calls to the method


replace with inline code
test
remove method definition

Refactoring
????
????
????
????
????
????
????
????
????

init()
destroy()
legalComputerMove()
legalUserMove () *acceptably cohesive
*less than 20 lines
gameStatus ()
*accomplishes one functionality
resetFirst()
*testable with one unit test
paint ()
* Indicates that it meets criteria
mouseReleased()
bestMove() - more than 20 lines

Refactoring
**** init()
public void init() { // initialize applet, load images, add listener
setComputerImage(getImage(getCodeBase(), "oimage.gif"));
setUserImage(getImage(getCodeBase(), "ximage.gif"));
addMouseListener(this);
} // end init
*Cohesive acceptable - modifies two class variables indirectly
*Size: acceptable
*One functionality: TWO set up images and add listener
what if we wanted to implement a phone interface.
*One unit test: neet two tests one for images and one for listenert

Refactoring
**** destroy()
public void destroy() { // destroy listener needed for closing
removeMouseListener(this);
} // end destroy
*Cohesive modifies no class variables - acceptable
*Size acceptable
*One function only one acceptable
*One unit test only one acceptable

Refactoring
**** init()
**** destroy()
???? legalComputerMove()
???? legalUserMove ()
*acceptably cohesive
???? gameStatus ()
*less than 20 lines
*accomplishes one functionality
???? resetFirst()
*testable with one unit test
???? paint ()
???? mouseReleased()
???? bestMove() - more than 20 lines

Refactoring
???? legalUserMove ()

boolean legalUserMove(int computerStatus, int userStatus, int canidateMo


{
if ((canidateMove < 0) || (canidateMove > 8)) { return false; } // end if
if (((userStatus | computerStatus) & (1 << canidateMove)) != 0)
{ return false;} // end if
setUserStatus (userStatus | 1 << canidateMove);
return true;
} // end legalUserMove
*Cohesive modifies userStatus indirectly - acceptable
*Size acceptable
*Only One function NO Test legal AND sets user status
*Testable two types of tests for two function
LOOK at where this method is callled to see if we can separate.

Refactoring
???? legalUserMove ()
boolean legalUserMove(int computerStatus, int userStatus, int
canidateMove) {
if ((canidateMove < 0) || (canidateMove > 8)) { return false; } // end if
if (((userStatus | computerStatus) & (1 << canidateMove)) != 0)
{return false;} // end if
setUserStatus (userStatus | 1 << canidateMove);
return true;
} // end legalUserMove

if (legalUserMove(getComputerStatus(), getUserStatus(), col + row * 3)) {


repaint();
.

Refactoring
**** legalUserMove ()

boolean legalUserMove(int computerStatus, int userStatus, int canidateMo


{ // user move return true if legal move
if ((canidateMove < 0) || (canidateMove > 8)) { return false;} // end if
if (((userStatus | computerStatus) & (1 << canidateMove)) != 0) {
return false;
} // end if
return true;
} // end legalUserMove

int canidateMove = col + row * 3;


if (legalUserMove(getComputerStatus(), getUserStatus(), canidateMove)
setUserStatus (userStatus | 1 << canidateMove);

Refactoring
???? legalComputerMove()

boolean legalComputerMove(int computerStatus, int userStatus) {


if ((userStatus | computerStatus) == ENDINGSTATE) {return false;} //end
setComputerStatus(computerStatus|1<<bestMove(computerStatus,userStat
return true;
} // end legalComputerMove
Cohesive = modifies computerStatus indirectly -- acceptable
Size acceptable
One function NO it test if legal AND sets computer status
Testing - two types of tests one for the legality and one for set
Again LOOK at where this method is called to see if we can separate

Refactoring
**** legalComputerMove()
boolean legalComputerMove(int computerStatus, int userStatus) {
if ((userStatus | computerStatus) == ENDINGSTATE) {return false;} //
setComputerStatus
(computerStatus | 1 << bestMove(computerStatus, userStatus));
return true;
} // end legalComputerMove
if (legalComputerMove(getComputerStatus(), getUserStatus())) {
repaint();

Refactoring
**** legalComputerMove()
boolean legalComputerMove(int computerStatus, int userStatus) {
if ((userStatus|computerStatus)==ENDINGSTATE) {return false; } // end
return true;
} // end legalComputerMove
if (legalComputerMove(getComputerStatus(), getUserStatus())) {
setComputerStatus
(computerStatus | 1 << bestMove(getComputerStatus(),
getUserStatus()));

Refactoring
**** init()
**** destroy()
**** legalComputerMove()
**** legalUserMove ()
*acceptably cohesive
???? gameStatus ()
*less than 20 lines
*accomplishes one functionality
???? resetFirst()
*testable with one unit test
???? paint ()
???? mouseReleased()
???? bestMove() - more than 20 lines

Refactoring
****

gameStatus ()

int gameStatus(int computerStatus, int userStatus ) {


if (winningState[computerStatus]) { return WIN;} // end if
if (winningState[userStatus]) { return LOSE;
} // end if
if ((userStatus | computerStatus) == ENDINGSTATE) {
return STALEMATE;
} // end if
return CONTINUE;
Cohesive = no modifications -- acceptable
} // end gameStatus
Size acceptable
One function acceptable
Testing - one test acceptable (4 test needed.

Refactoring
???? resetFirst()
public boolean resetFirst (boolean computerFirst) {
if (computerFirst) { // reset who is first
setComputerStatus ( 1 << (int)(Math.random() * 9));
}// end if
return !computerFirst;
} // end resetStatus
Cohesive = modifies one indirectly -- acceptable
Size acceptable
One function NO resets first AND sets computer status
Testing - two types of tests one for reset and one for the set
LOOK at where this method is called to see if we can separate

Refactoring
**?? resetFirst()
public boolean resetFirst (boolean computerFirst) {
if (computerFirst) { // reset who is first
setComputerStatus ( 1 << (int)(Math.random() * 9));
}// end if
return !computerFirst;
} // end resetStatus
\

setComputerFirst (resetFirst(getComputerFirst()));

Refactoring
**** resetFirst()
if (computerFirst) {
setComputerStatus ( 1 << (int)(Math.random() * 9));
}// end if
setComputerFirst (!computerFirst);
It makes more sense to take move the code back into the
calling method and look (our case) or relook (if already
evaluated) at the calling method for characteristics of

Refactoring
*acceptably cohesive
*less than 20 lines
*accomplishes one functionality
*testable with one unit test

**** init()
**** destroy()
**** legalComputerMove()
**** legalUserMove ()
**** gameStatus ()
**** resetFirst()
???? paint ()
???? mouseReleased()
???? bestMove() - more than 20 lines

Refactoring
???? paint ()
public void paint(Graphics g) { // paint the screen
Dimension d = getSize();
g.setColor(Color.black);
int xoff = d.width / NUMBER_OF_ROWS;
int yoff = d.height / NUMBER_OF_COLUMNS;
g.drawLine(xoff, 0, xoff, d.height); // draw first horiz line
g.drawLine(2*xoff, 0, 2*xoff, d.height); // draw second line
g.drawLine(0, yoff, d.width, yoff); // draw first verticle line
g.drawLine(0, 2*yoff, d.width, 2*yoff); // draw second line

Refactoring
???? paint () **continued

int i = 0;
for (int row = 0 ; row < NUMBER_OF_ROWS ; row++) {draw images
for (int col = 0 ; col < NUMBER_OF_COLUMNS ; col++, i++) {
if ((getComputerStatus() & (1 << i)) != 0) { // if square take
g.drawImage(getComputerImage(), col*xoff + 1,row*yoff + 1, thi
} else if ((getUserStatus() & (1 << i)) != 0) { // if user square taken
g.drawImage(getUserImage(), col*xoff + 1, row*yoff + 1, th
} // end if
}// end for
} // end for
} // end paint

Refactoring
*??* paint () **continued
Cohesive sets no class variables acceptable
Size somewhat long
Functionality - two exist
1) draw the grid
2) draw the images
Testing would need two tests for each each
function except both are gui
so these are functional tests
SO we will separate the functions

Refactoring
THE NEW methods
public void drawGrid(Graphics g, Dimension d, int xoff, int yoff){
g.drawLine(xoff, 0, xoff, d.height); // draw first horizontal line
g.drawLine(2*xoff, 0, 2*xoff, d.height); // draw second horizontal line
g.drawLine(0, yoff, d.width, yoff); // draw first verticle line
g.drawLine(0, 2*yoff, d.width, 2*yoff); // draw second verticle line
} // end drawGrid

Refactoring
THE NEW methods

public void drawImages(Graphics g, Dimension d, int xoff, int yoff){


int i = 0;
for (int row = 0 ; row < NUMBER_OF_ROWS ; row++) { // draw computer and user images
for (int col = 0 ; col < NUMBER_OF_COLUMNS ; col++, i++) {
if ((getComputerStatus() & (1 << i)) != 0) { // if computer bitmask square taken
g.drawImage(getComputerImage(), col*xoff + 1, row*yoff + 1, this);
} else if ((getUserStatus() & (1 << i)) != 0) { // if user bitmask square taken
g.drawImage(getUserImage(), col*xoff + 1, row*yoff + 1, this);
} // end if
}// end for
} // end for
} // end draw Images

Refactoring
**** paint () THE NEW paint
public void paint(Graphics g) { // paint the screen
Dimension d = getSize();
g.setColor(Color.black);
int xoff = d.width / NUMBER_OF_ROWS;
int yoff = d.height / NUMBER_OF_COLUMNS;
drawGrid(g, d, xoff, yoff);
drawImages (g, d, xoff, yoff);
}// end paint

Refactoring
*acceptably cohesive
*less than 20 lines
*accomplishes one functionality
*testable with one unit test

**** init()
**** destroy()
**** legalComputerMove()
**** legalUserMove ()
**** gameStatus ()
**** resetFirst()
**** paint ()
???? mouseReleased()
???? bestMove() - more than 20 lines

Refactoring
???? mouseReleased()
public void mouseReleased(MouseEvent e) { // user clicked applet
int x = e.getX(); // get mouse x location
int y = e.getY(); // get mouse y location

Many functions are included in this method and we will extract this
functionality in methods

Refactoring
// this code checks if the game is over on the previous move
switch (gameStatus(getComputerStatus(), getUserStatus())) {
case WIN:
case LOSE:
case STALEMATE:
play(getCodeBase(), "audio/return.au");
setComputerStatus(0);
setUserStatus(0);
if (computerFirst) {
setComputerStatus ( 1 << (int)(Math.random() * 9));
}// end if
setComputerFirst (!computerFirst);
repaint();
return;

Refactoring
// find out where the click occurred
Dimension d = getSize();
int col = (x * NUMBER_OF_COLUMNS) / d.width;
int row = (y * NUMBER_OF_ROWS) / d.height;

Refactoring
// determine if user move causes a win or stalemate
if (legalUserMove(getComputerStatus(),getUserStatus(),col+row*3)) {
repaint();
switch (gameStatus(getComputerStatus(), getUserStatus())) {
case WIN:
System.out.println ("I win");
break;
case LOSE:
System.out.println ("You win");
break;
case STALEMATE:
System.out.println ("No Winner");
break;

Refactoring
// find a legal computer move and see if it wins or stales
default:
if (legalComputerMove(getComputerStatus(), getUserStatus())) {
repaint();
switch (gameStatus(getComputerStatus(), getUserStatus())) {
case WIN:
System.out.println ("I win this move");
break;
case LOSE:
System.out.println ("You win this move");
break;
case STALEMATE:
System.out.println ("No Winner this move");
break;
default:

Refactoring
???? mouseReleased()
Cohesive sets computerStatus, computerFirst, userStatus
Size somewhat long
Functionality - many functions some may be reusable
Testing would need many tests
SO we will separate the functions

Refactoring
???? gameOver()
public boolean gameOver(int status) {
// check and reset if a WIN, LOSE, or STALEMATE after computer move
switch (status) {
case WIN:
case LOSE:
case STALEMATE:
play(getCodeBase(), "audio/return.au");
setComputerStatus(0);
setUserStatus(0);
if (computerFirst) { setComputerStatus ( 1 << (int)(Math.random() * 9));
}// end if
setComputerFirst (!computerFirst);
*Cohesive NO all sets here
repaint();
*Size - acceptables
return true;
default:
return false;
*One functionality - no many
} // end switch
*Testable no many
} // end gameOver

Refactoring
???? resetGame()
public void resetGame () {
play(getCodeBase(), "audio/return.au");
setComputerStatus(0);
setUserStatus(0);
if (computerFirst) {
setComputerStatus ( 1 << (int)(Math.random() * 9));
}// end if
setComputerFirst (!computerFirst);
} // end resetGame
*Cohesive NO all sets here
*Size - acceptable
*One functionality - no many
*Testable yes only one not a set

Refactoring
**** playComputerIffirst()
public void playComputerIfFirst ( ) {
if (getComputerFirst()) {
setComputerStatus ( 1 << (int)(Math.random() * 9));
}// end if
} // end playComputerIfFirst
*Cohesive only indirectly sets one class variable
*Size - acceptable
*One functionality - yes
*Testable yes

Refactoring
**** resetGame()
public void resetGame () {
play(getCodeBase(), "audio/return.au");
setComputerStatus(0);
setUserStatus(0);
playComputerifFirst ( );
setComputerFirst (!getComputerFirst());
} // end resetGame
*Cohesive acceptable ONLY sets
*Size - acceptable
*One functionality - only sets
*Testable yes sets

Refactoring
???? mouseReleased() new version 2
public void mouseReleased(MouseEvent e) { // user clicked applet
int x = e.getX(); // get mouse x location
int y = e.getY(); // get mouse y location
Dimension d = getSize();
int col = (x * NUMBER_OF_COLUMNS) / d.width; // determine col
int row = (y * NUMBER_OF_ROWS) / d.height; // determine row
if (gameOver (gameStatus(getComputerStatus(), getUserStatus()))) {
resetGame();
repaint();
return;
} // end if

Refactoring
mouseReleased () VERSION 2 CONTINUED
// determine if user move causes a win, loose or stalemate and post it
if (legalUserMove(getComputerStatus(),getUserStatus(),col+row*3)) {
repaint();
switch (gameStatus(getComputerStatus(), getUserStatus())) {
case WIN:
System.out.println ("I win");
break;
case LOSE:
System.out.println ("You win");
break;
case STALEMATE:
System.out.println ("No Winner");
break;

Refactoring
// find a legal computer move and see if it wins or stales // VERSION 2
default:
if (legalComputerMove(getComputerStatus(), getUserStatus())) {
repaint();
switch (gameStatus(getComputerStatus(), getUserStatus())) {
case WIN:
System.out.println ("I win this move");
break;
case LOSE:
System.out.println ("You win this move");
break;
case STALEMATE:
System.out.println ("No Winner this move");
break;
default:

Refactoring
???? postGameStatus()
public void postGameStatus (int status) {
switch (status) {
case WIN:
System.out.println ("I win #1");
break;
case LOSE:
System.out.println ("You win #1");
break;
case STALEMATE:
System.out.println ("No Winner #1");
*Cohesive no modifies acceptable
break;
*Size - acceptable
default:
*One functionality - yes acceptable
} // end switch
*Testable yes one type (3 tests)
} // end checkGameStatus

Refactoring
???? VERSION 3 of mouseReleased()
public void mouseReleased(MouseEvent e) { // user clicked applet
int x = e.getX(); // get mouse x location
int y = e.getY(); // get mouse y location
Dimension d = getSize();
int col = (x * NUMBER_OF_COLUMNS) / d.width; / determine col
int row = (y * NUMBER_OF_ROWS) / d.height; // determine row
if (gameOver (gameStatus(getComputerStatus(), getUserStatus()))) {
resetGame();
repaint();
return;

Refactoring
???? REMAINDER OF mouseReleased() VERSION 3

int canidateMove = col + row * 3;


if (legalUserMove(getComputerStatus(), getUserStatus(), canidateMove)) {
setUserStatus (userStatus | 1 << canidateMove);
repaint();
int status = gameStatus(getComputerStatus(), getUserStatus());
if (!(status == CONTINUE)) {
postGameStatus (status); }
else {
if (legalComputerMove(getComputerStatus(), getUserStatus())) {
setComputerStatus(computerStatus|1<<bestMove(getComputerStatus(),getUserSt
repaint();
postGameStatus(gameStatus(getComputerStatus(), getUserStatus()));
} else { play(getCodeBase(), "audio/beep.au"); } // end else
} // end else
} else { // not legal user move
play(getCodeBase(), "audio/beep.au");
}// end else

Refactoring
???? mouseReleased() VERSION 3

*Cohesive sets two class variables indirectly ?


*Size still somewhat large
*One functionality - no several functions
tries to moveUser and moveComputer
*Testable no several types per function

Refactoring
???? mouseReleased() VERSION 4
public void mouseReleased(MouseEvent e) { // user clicked applet
int x = e.getX(); // get mouse x location
int y = e.getY(); // get mouse y location
Dimension d = getSize();
int col = (x * NUMBER_OF_COLUMNS) / d.width; get col
int row = (y * NUMBER_OF_ROWS) / d.height; // determine row
if (gameOver (gameStatus(getComputerStatus(), getUserStatus()))) {
resetGame();
repaint();
return;
} // end if

Refactoring
???? mouseReleased() VERSION 4 CONTINUED
int canidateMove = col + row * 3;
if (legalUserMove(getComputerStatus(), getUserStatus(), canidateMove))
repaint();
setUserStatus (userStatus | 1 << canidateMove);
int status = gameStatus(getComputerStatus(), getUserStatus());
if (status == CONTINUE) {
if (legalComputerMove(getComputerStatus(), getUserStatus())) {
repaint();
moveComputer();
} else { play(getCodeBase(), "audio/beep.au"); } // end else
} else { postGameStatus (status); } // end else
} else { play(getCodeBase(), "audio/beep.au"); }// end else // not legal
Still not good but best we can do

Refactoring
*acceptably cohesive
*less than 20 lines
*accomplishes one functionality
*testable with one unit test

**** init()
**** destroy()
**** legalComputerMove()
**** legalUserMove ()
**** gameStatus ()
**** resetFirst()
**** paint ()
**?? mouseReleased()
???? bestMove()

Refactoring
???? bestMove ()
int bestMove(int computerStatus, int userStatus) {
int mostStrategicMove[] = {4,0,2,6,8,1,3,5,7};
int bestmove = bestMoveNotFound;
loop:
for (int i = firstCell ; i <= lastCell ; i++) {
int potentialComputerMove = mostStrategicMove[i];
if (((computerStatus & (1 << potentialComputerMove)) == 0) &&
((userStatus & (1 << potentialComputerMove)) == 0)) {
int potentialComputerStatus =
computerStatus | (1 << potentialComputerMove);
if (winningState[potentialComputerStatus]) { // computer wins
return potentialComputerMove;
} /// end if (not user taken ) && ( not computer taken )

Refactoring
???? bestMove () CONTINUED

for (int j = firstCell ; j <= lastCell ; j++) { // for square = 0 to < 9


if (((potentialComputerStatus & (1 << j)) == 0) &&
((userStatus & (1 << j)) == 0)) {
int potentialUserStatus = userStatus | (1 << j);
if (winningState[potentialUserStatus]) { // user wins, take anothe
continue loop;
} // end if won
} // end if &&
} // end for
if (bestmove == bestMoveNotFound) { // neither wins, this move will
do
bestmove = potentialComputerMove;
} // end if
} // end if &&

Refactoring
???? bestMove () CONTINUED
*Cohesive sets no class variables
*Size somewhat large
*One functionality - yes
*Testable one type (several tests)

Refactoring
???? userWins ()
public boolean userWins (int potentialComputerStatus) {
for (int j = firstCell ; j <= lastCell ; j++) { // for square = 0 to < 9
if (((potentialComputerStatus & (1 << j)) == 0) &&
((userStatus & (1 << j)) == 0)) {
int potentialUserStatus = userStatus | (1 << j);
if (winningState[potentialUserStatus]) {
// user wins, take anothe
return true;
} // end if won
*Cohesive no changes to class var
} // end if &&
*somewhat long
} // end for
*one function yes one
return false;
*testable - yes one type of test
} // end checkIfUserWon

Refactoring
???? bestMove ()

VERSION 2

int bestMove(int computerStatus, int userStatus) {


int mostStrategicMove[] = {4,0,2,6,8,1,3,5,7};
int bestmove = bestMoveNotFound;
loop:

Refactoring
???? bestMove ()

VERSION 2 CONTINUED
for (int i = firstCell ; i <= lastCell ; i++) {
int potentialComputerMove = mostStrategicMove[i];
if (((computerStatus & (1 << potentialComputerMove)) == 0) &&
((userStatus & (1 << potentialComputerMove)) == 0)) {
int potentialComputerStatus =
computerStatus | (1 << potentialComputerMove);
if (winningState[potentialComputerStatus]) { // computer wins
return potentialComputerMove;
} /// end if (not user taken ) && ( not computer taken )
if (userWins (potentialComputerStatus)) { continue loop;}
if (bestmove == bestMoveNotFound) { // neither can win
bestmove = potentialComputerMove;
} // end if
} // end if &&

Refactoring
???? bestMove ()

VERSION 2 CONTINUED
if (bestmove != bestMoveNotFound) { // return best one
return bestmove;
} // end if bestmove
for (int i = firstCell ; i <= lastCell ; i++) { // try first one open
int firstAvailableComputerMove = mostStrategicMove[i];
if (((computerStatus & (1 << firstAvailableComputerMove)) == 0)
&& ((userStatus & (1 << firstAvailableComputerMove)) == 0)) {
return firstAvailableComputerMove;
} // end if &&
} // end for
return bestMoveNotFound; // return no more moves
} // end best move

Refactoring
**** bestMove () CONTINUED
*Cohesive sets no class variables
*Size still somewhat large but tolerable
*One functionality - yes
*Testable yes one type several tests

Refactoring

Conditionals

Refactoring
Conditionals should not be too
complex. If they are complex,
refactor them to make simple
methods making readability and
understandability better.

Refactoring
FOR EXAMPLE
int bestMove(int computerStatus, int userStatus) {

if (((computerStatus & (1 << potentialComputerMove)) ==


0) && ((userStatus & (1 << potentialComputerMove)) == 0))
if (((potentialComputerStatus & (1 << j)) == 0) &&
((userStatus & (1 << j)) == 0))
if (((computerStatus & (1 <<firstAvailableComputerMove))
== 0) && ((userStatus & (1 <firstAvailableComputerMove))

Refactoring
REFACTORINGS
Decomposing Conditional
Consolidating Conditional Statements
Consolidate Duplicate Conditional Fragments
Replace Nested Conditional with Guard Clauses
Remove Control Flag
Not all of these are covered in detail.
Rather the resulting code for TTT is shown.

Refactoring
if (((computerStatus & (1 << firstAvailableComputerMove)) == 0)
&& ((userStatus & (1 << firstAvailableComputerMove)) == 0))

BECOMES
public boolean cellEmpty
(int computerStatus,int potentialComputerMove,int userStatus) {
return ((computerStatus & (1 << potentialComputerMove)) == 0)
&& ((userStatus & (1 << potentialComputerMove)) == 0);
} // end cellEmpty

Refactoring
if ((getComputerStatus() & (1 << i)) != 0)

BECOMES
public boolean squareOccupied (int i, int playerStatus) {
return ((playerStatus & (1 << i)) != 0);
} // end squareOccupied

WITH

REUSED
3 times

if (squareOccupied (i, getComputerStatus()))

You might also like