Refactoring
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
C.
How do we Refactor?
Refactoring is accomplished by
1)
2)
3)
4)
Refactoring
D. When do we Refactor?
Refactoring is done when
1)
2)
3)
4)
5)
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
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
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
Refactoring
Refactoring Opportunities
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
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
Refactoring
MOVE METHOD
and rename
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
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
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
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()
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
Refactoring
Original Code --
page 3
} Sub-types
Self Encapsulate
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);
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
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; }
Refactoring
Use POLYMORPHISM
page 47
class Price
abstract double getCharge (int daysRented);
Refactoring
We will look at several types of refactoring.
These include the refactoring of:
methods
generalization
classes
data
calls
conditional expressions
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
Encapsulate Collection
Replace Subclass
with Field
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
Add Parameter
Remove Parameter
Hide Method
Parameterize Method
Encapsulate Downcast
Refactoring
Refactoring by Dealing with Generalization
Pull Up Field
Extract Interface
Pull Up Method
Collapse Hierarchy
Delegation
Extract Subclass
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
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
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
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.
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
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
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.
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
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
Refactoring
Replace Magic Number with
Symbolic Constant:
Motivation:
Magic numbers are numbers with special values that
usually are not obvious.
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
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:
= platform.toUpperCase().indexOf(MAC)
= platform.toUpperCase().indexOf(IE) >
= 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
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:
Refactoring
5. Split Temporary Variables:
Example:
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
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
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:
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:
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:
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:
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);
.
Refactoring
1. Extract Method:
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:
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
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:
Refactoring
2. Inline Method:
Example:
Refactoring
2. Inline Method:
Mechanics:
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 ()
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
Refactoring
**** legalUserMove ()
Refactoring
???? legalComputerMove()
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 ()
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
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
Refactoring
???? mouseReleased() VERSION 3
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
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
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) {
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