Refactoring

advertisement
Refactoring
This lecture is divided into and
introduction to refactoring and
then several lessons.
The intent is not to teach you all
the refactorings but to expose you
to just a few…
Refactoring
Introduction Outline
•
•
•
•
A.
B.
C.
D.
What is Refactoring?
Why do we Refactor?
How do we Refactor?
When do we Refactor?
Refactoring
A.
What is Refactoring?
Refactoring is a technique which identifies
bad code (code that smells) and allows
promotes the re-structuring 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
B. Why do we Refactor?
Refactoring allows building of complex
systems by exploiting sophisticated objectoriented techniques that yield such systems
catagorized as frameworks, beans, or other
reusable software components.
Refactoring
Why do you refactor
Enable sharing of logic.
Explain intention and implementation seperately.
Isolate change.
Encode conditional logic.
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
When do you refactor
Refactor when you add functions.
Refactor as you do a code review.
Refactor when you fix a bug.
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 improves the design of software.
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
Code --
page 3
public class Movie {
public static final int CHILDRENS = 2; // type movie for children
public static final int REGULAR = 0; // type of regular movie
public static final int NEW_RELEASE = 1; // new release movie
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;
}// end constructor
Constructor
public int getPriceCode() { return priceCode; }
public void setPriceCode (int agr) { _priceCode = arg; }
public String getTitle () { return _Title; }
} // end Movie
pricecode: int
children = 2
regular = 0
new_release=1
title: String
getPriceCode)_
setPriceCode()
getTitle()
Refactoring
Rental
daysRented:int
Code (con’t)--
page 3
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
getMovie()
getDaysRented)_
Refactoring
Customer
name: String
rentals: vector
Code (con’t)--
page 4
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
public Customer (String name) { _name = name; }
Constructor
public void addRental (Rental arg) { _rentals.addElement(arg) }
public String getName () { return _name}
Refactoring
Code (con’t)--
page 5
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
Code (Con’t) page 5
// 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
Code (Con’t) page 5
// 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
• This method, called statement, is TOO
LARGE.
• This statement method should not be inside
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();
thisAmount = amountFor(each);
Calls the new method
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();
thisAmount = amountFor(each);
Should NOT be in customer class
Should be in the rental
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;
} // end getCharge
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;
Rental each = (Rental) rentals.nextElement();
thisAmount = each.getCharge(each);
page 19
Calls the new method
In the rental class
Refactoring
An Example – Sequence Diagram
Customer
Rental
Movie
statement
forallrentals
Rental: getMovie()
amount:
getCharge()
getPriceCode()
getDaysRented()
Refactoring
Refactoring Opportunities
a code cluster is setting one variable
EXTRACT it as a METHOD
returning the variable
}
// 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
Refactoring
EXTRACT METHOD
// add frequent renter points add 1 frequent renter point if NEW
RELEASE rented > one day
int getFrequentRenterPoints() {
if ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) &&
each.getDaysRented() > 1) return 2;
else return 1;
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();
frequentRenterPoints += getFrequentRenterPoints();
Should NOT be in customer class
Should be in the rental
Refactoring
MOVE METHOD
class Rental…..
…..
// add frequent renter points add 1 frequent renter point if NEW RELEASE
rented > one day
int getFrequentRenterPoints() {
if ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) &&
each.getDaysRented() > 1) return 2;
else return 1;
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;
Rental each = (Rental) rentals.nextElement();
thisAmount = each.getCharge();
frequentRenterPoints += each.getFrequentRenterPoints();
Refactoring
Refactoring Opportunities
a variable is used temporarily
REPLACE TEMP with QUERY
eliminating the temp
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();
thisAmount = each.getCharge();
frequentRenterPoints += getFrequentRenterPoints();
// show figures for this rental
result +=“/t” + each.getMovie().getTitle*(+”\t” +
String.valueOf(thisAmount) + “\n”;
totalAmount +=this.Amount;
} // end while
Refactoring
REPLACE TEMP with QUERY
page 21
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();
thisAmount = each.getCharge();
frequentRenterPoints += getFrequentRenterPoints();
// show figures for this rental
result +=“/t” + each.getMovie().getTitle*(+”\t” +
String.valueOf(thisAmount each.getCharge()) + “\n”;
totalAmount += this.Amount each.getCharge() ;
} // end while
Refactoring
Refactoring Opportunities
a variable is used temporarily
REPLACE TEMP with QUERY
eliminating the temp
public String statement() {
double totalAmount = 0;
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”;
totalAmount += each.getCharge();
} // end loop
// add footer lines
result +=“Amount owed is “ + String.valueOf(totalAmount)
+ “\n”;
Problem – temp in loop
result += “You earned “ + String.valueOf(frequentRenterPoints) + “ frequent renter
points”;
return result;
} // end statement
Refactoring
EXTRACT it as a METHOD
private double getTotalCharge() {
double result = 0;
Enumeration rentals = _rentals.elements();
while (rentals.hasMoreElements() {
Rental each = (Rental) rentals.nextElement();
result += each.getCharge();
} // end loop
return result;
} // end getTotalCharge
Refactoring
REPLACE TEMP with QUERY
page 27
public String statement() {
double totalAmount = 0;
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”;
totalAmount += each.getCharge();
}
// add footer lines
result +=“Amount owed is “ + String.valueOf( totalAmount getTotalCharge()) + “\n”;
result += “You earned “ + String.valueOf(frequentRenterPoints) + “ frequent renter points”;
return result;
} // end statement
Yes, we are looping twice
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
REPLACE TEMP with QUERY
public String statement() {
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”;
}
// add footer lines
result +=“Amount owed is “ + String.valueOf( getTotalCharge()) + “\n”;
result += “You earned “ + String.valueOf(frequentRenterPoints
getTotalFrequentRentalPoints ) + “ frequent renter points”;
return result;
} // end statement
Yes, we are looping thrice
page 29
Refactoring
An Example – Sequence Diagram
Customer
Rental
Movie
statement
getTotalCharge
amount: getCharge()
getPriceCode()
getFrequentRenterPoints()
getPriceCode()
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
Original- 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
State Pattern- Class Diagram
Price
1..1
Movie
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
Self Encapsulating Movie Code -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
page 41
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
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.
page 47
Refactoring
Use POLYMORPHISM
page 47
class Price
abstract double getCharge (int daysRented);
Create an overiding method for the getCharge method.
Refactoring
Use POLYMORPHISM
page 48
class Movie…..
int getFrequentRenterPoints (int daysRented) {
if ((getPriceCode() == Movie.NEW_RELEASE) && daysRented >1)
return 2;
else
return 1;
}
We can do the same thing with getFrequentRenterPoints
Refactoring
Final Thoughts
page 52
Placing a state pattern in code is quite an effort
The gain is that if I
change any of the price’s behavior
add new prices
add extra price dependent behavior
The rest of the application does not know about the use of the state pattern.
For this tiny bit of behavior, it does not seem worthwhile.
These changes lead to better distributed responsibilities and code
1that is easier to maintain.
It does look different than the regular procedural code.
One important lesson in this example is the rhythm of refactgoring,
change a little
test a little
Refactoring
Catalogue of Refactoring
We will look at a
catalogue of refactoring methods.
Each refactoring method is described by the following:
name
: noun (with a page number of the text)
summary
: narrative description
motivation
: why you would use the technique
example
: code using the technique
mechanics
: how you would use the technique
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
Replace Type Code with State/Strategy
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
Because there are SOOOO many factorings
It if difficult to know how to approach
refactoring the code you have written. And
unfortunately, we have not progressed in
teaching programming to notice needed
refactorings.
You can approach it using a few simple
guidelines.
Refactoring
Refactoring Topics
First: Make your code Self-Documenting
Second: Encapsulate your classes
Third: Assure Constants and Variables are coded correctly
Fourth: Make sure you have GOOD Methods
Fifth: Make sure your Conditionals are coded correctly
Sixth: Assure your Classes are coded correctly
Seventh: Assure proper Inheritance
Eighth: Apply needed Patterns
Refactoring
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.net.*;
import java.applet.*;
/**
* A Simple TicTacToe applet.
* A Tic Tac Toe applet.
* A very simple, and mostly brain-dead
* implementation of your favorite game!
*/
Refactoring
/**
*
* In this game a position is represented by a white and black
* bitmask. A bit is set if a position is occupied. There are
* 9 squares so there are 1<<9 possible positions for each
* side. An array of 1<<9 Booleans is created, it marks
* all the winning positions.
*
* @version 1.2, 13 Oct 1995
* @author Arthur van Hoff
* @modified 96/04/23 Jim Hagen : winning sounds
* @modified 03/07/21 Sara Stoecklin : updated java version
*/
Refactoring
public
class TTTV0 extends Applet implements MouseListener {
/**
* White's current position. The computer is white.
*/
int white;
/**
* Black's current position. The user is black.
*/
int black;
Refactoring
/**
* The squares in order of importance...
*/
final static int moves[] = {4, 0, 2, 6, 8, 1, 3, 5, 7};
/**
* The winning positions.
*/
static boolean won[] = new boolean[1 << 9];
static final int DONE = (1 << 9) - 1;
static final int OK = 0;
static final int WIN = 1;
static final int LOSE = 2;
static final int STALEMATE = 3;
Refactoring
/**
* Mark all positions with these bits set as winning.
*/
static void isWon(int pos) {
for (int i = 0 ; i < DONE ; i++) {
if ((i & pos) == pos) {
won[i] = true;
}
}
}
Refactoring
/**
* Initialize all winning positions.
*/
static {
isWon((1 << 0) | (1 << 1) | (1 << 2));
isWon((1 << 3) | (1 << 4) | (1 << 5));
isWon((1 << 6) | (1 << 7) | (1 << 8));
isWon((1 << 0) | (1 << 3) | (1 << 6));
isWon((1 << 1) | (1 << 4) | (1 << 7));
isWon((1 << 2) | (1 << 5) | (1 << 8));
isWon((1 << 0) | (1 << 4) | (1 << 8));
isWon((1 << 2) | (1 << 4) | (1 << 6));
}
Refactoring
/**
* Compute the best move for white.
* @return the square to take
*/
int bestMove(int white, int black) {
int bestmove = -1;
Refactoring
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]) {
// white wins, take it!
return mw;
}
Refactoring
loop:
…….
for (int mb = 0 ; mb < 9 ; mb++) {
if (((pw & (1 << mb)) == 0) &&
((black & (1 << mb)) == 0)) {
int pb = black | (1 << mb);
if (won[pb]) {
// black wins, take another
continue loop;
}
}
}
Refactoring
loop:
……
……..
// Neither white nor black can win in one move, this will do.
if (bestmove == -1) {
bestmove = mw;
}
}
}
if (bestmove != -1) {
return bestmove;
}
Refactoring
loop:
……
……
……
// No move is totally satisfactory, try the first one that is open
for (int i = 0 ; i < 9 ; i++) {
int mw = moves[i];
if (((white & (1 << mw)) == 0) &&
((black & (1 << mw)) == 0)) {
return mw;
}
}
// No more moves
return -1;
}
Refactoring
/**
* User move.
* @return true if legal
*/
boolean yourMove(int m) {
if ((m < 0) || (m > 8)) {
return false;
}
if (((black | white) & (1 << m)) != 0) {
return false;
}
black |= 1 << m;
return true;
}
Refactoring
/**
* Computer move.
* @return true if legal
*/
boolean myMove() {
if ((black | white) == DONE) {
return false;
}
int best = bestMove(white, black);
white |= 1 << best;
return true;
}
Refactoring
/**
* Figure what the status of the game is.
*/
int status() {
if (won[white]) {
return WIN;
}
if (won[black]) {
return LOSE;
}
if ((black | white) == DONE) {
return STALEMATE;
}
return OK;
}
Refactoring
/**
* Who goes first in the next game?
*/
boolean first = true;
/**
* The image for white.
*/
Image notImage;
/**
* The image for black.
*/
Image crossImage;
Refactoring
/**
* Initialize the applet. Resize and load images.
*/
public void init() {
notImage = getImage(getCodeBase(), "oimage.gif");
crossImage = getImage(getCodeBase(), "ximage.gif");
addMouseListener(this);
}
public void destroy() {
removeMouseListener(this);
}
Refactoring
/**
* Paint it.
*/
public void paint(Graphics g) {
Dimension d = getSize();
g.setColor(Color.black);
int xoff = d.width / 3;
int yoff = d.height / 3;
g.drawLine(xoff, 0, xoff, d.height);
g.drawLine(2*xoff, 0, 2*xoff, d.height);
g.drawLine(0, yoff, d.width, yoff);
g.drawLine(0, 2*yoff, d.width, 2*yoff);
Refactoring
int i = 0;
for (int r = 0 ; r < 3 ; r++) {
for (int c = 0 ; c < 3 ; c++, i++) {
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);
}
}
}
}
Refactoring
/**
* The user has clicked in the applet. Figure out where
* and see if a legal move is possible. If it is a legal
* move, respond with a legal move (if possible).
*/
public void mouseReleased(MouseEvent e) {
int x = e.getX();
int y = e.getY();
Refactoring
switch (status()) {
case WIN:
case LOSE:
case STALEMATE:
play(getCodeBase(), "audio/return.au");
white = black = 0;
if (first) {
white |= 1 << (int)(Math.random() * 9);
}
first = !first;
repaint();
return;
}
Refactoring
// Figure out the row/column
Dimension d = getSize();
int c = (x * 3) / d.width;
int r = (y * 3) / d.height;
if (yourMove(c + r * 3)) {
repaint();
Refactoring
switch (status()) {
case WIN:
play(getCodeBase(), "audio/yahoo1.au");
break;
case LOSE:
play(getCodeBase(), "audio/yahoo2.au");
break;
case STALEMATE:
break;
Refactoring
default:
if (myMove()) {
repaint();
switch (status()) {
case WIN:
play(getCodeBase(), "audio/yahoo1.au");
break;
case LOSE:
play(getCodeBase(), "audio/yahoo2.au");
break;
case STALEMATE:
break;
default:
play(getCodeBase(), "audio/ding.au");
}
Refactoring
} else {
play(getCodeBase(), "audio/beep.au");
}
}
} else {
play(getCodeBase(), "audio/beep.au");
}
}
Refactoring
public void mousePressed(MouseEvent e) {
}
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public String getAppletInfo() {
return "TicTacToe by Arthur van Hoff";
}
}
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
My format- get rid of extra white space to show on screens
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.net.*;
import java.applet.*;
/**
* A Simple TicTacToe applet. A Tic Tac Toe applet.
* A very simple, and mostly brain-dead
* implementation of your favorite game!
*/
Refactoring
Rewrite comments to make them clearer
/**
* A bitmask is used for the two players denoting positions occupied.
* Each bit represents a square on the board 0..8.
* The bit is 0 if not occupied and 1 if occupied.
* Winning is determined by comparing the bitmask with
* an array of Booleans that marks all the winning positions.
* @version 1.2, 13 Oct 1995
* @author Arthur van Hoff
* @modified 96/04/23 Jim Hagen : winning sounds
* @modified 03/07/21 Sara Stoecklin : updated java version
*/
Refactoring
Place all class constants first in alphabetical order
public
class TTTV11 extends Applet implements MouseListener {
static final int DONE = (1 << 9) - 1; // sentinal for square loop
static final int LOSE = 2; // status for user wins
static final int OK = 0; // status for game continues
static final int STALEMATE = 3; // status for a tie
static final int WIN = 1; // status for computer wins
Refactoring
Place all class variables next in alphabetical order
int black; // user bitmask denotes user squares occupied
Image crossImage; // user image
boolean first = true; // who goes first next game
// The squares in order of importance... positions 0..8
final static int moves[] = {4, 0, 2, 6, 8, 1, 3, 5, 7};
Image notImage; // computer image
int white; // computer bitmask denotes squares occupied
static boolean won[] = new boolean[1 << 9]; // winning states
Refactoring
I move comments to save real estate
static void isWon(int pos) { // mark winning squares as true win
for (int i = 0 ; i < DONE ; i++) {
if ((i & pos) == pos) {
won[i] = true;
} // end if (i & pos)
} // end for
} // end isWon
Refactoring
Document all possible statements
static { // initialize winning squares by shifting a one n bits
isWon((1 << 0) | (1 << 1) | (1 << 2)); // row one win
isWon((1 << 3) | (1 << 4) | (1 << 5)); // row two win
isWon((1 << 6) | (1 << 7) | (1 << 8)); // row three win
isWon((1 << 0) | (1 << 3) | (1 << 6)); // col one win
isWon((1 << 1) | (1 << 4) | (1 << 7)); // col two win
isWon((1 << 2) | (1 << 5) | (1 << 8)); // col three win
isWon((1 << 0) | (1 << 4) | (1 << 8)); // dia right win
isWon((1 << 2) | (1 << 4) | (1 << 6)); // dia left win
} // end static
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
int best = bestMove(white, black);
BECOMES
int best = bestMove(computerStatus, userStatus);
if (((black|white)&(1<< m))!= 0) black |= 1 << m;
BECOMES
if (((userStatus|computerStatus)&(1<< m))!= 0) userStatus |= 1 << m;
if (((white&(1<< mw))== 0) &&((black&(1<<mw))== 0))
BECOMES
if (((computerStatus&(1<< mw))== 0) &&((userStatus&(1<<mw))== 0))
Refactoring
if ((black | white) == DONE)
BECOMES
if ((userStatus | computerStatus) == DONE)
if (won[white]) return WIN;
BECOMES
if (won[computerStatus]) return WIN;
if (won[black])
BECOMES
if (won[userStatus])
return LOSE;
return LOSE;
if ((black | white) == DONE)
BECOMES
if ((userStatus | computerStatus) == DONE)
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
won[] = new boolean[1 << 9];
BECOMES
winningState[] = new boolean[1 << 9]; // winning states of game
if (won[white]) BECOMES if (winningState[computerStatus])
if (won[black]) BECOMES if (winningState[userStatus])
if (won[pw]) BECOMES
if (winningState[pw])
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
const
const
const
const
const
int
int
int
int
int
ENDINGSTATE
LOSE
CONTINUE
STALEMATE
WIN
r
var
int
computerStatus
p
2
var
Image
userImage
var
boolean
computerFirst
1
3
var
arrayint
mostStrategicMove[]
var
Image
computerImage
var
int
userStatus
p
p
var
arrayint
winningState[]
u
r
r
p
p
ps
r
r
r
r
r
p
paint
init
mouseReleased
resetFirst
gameStatus
legalComputerMove
legalUserMove
bestMove
setWinningState
3. Adjust code to limit scope
r
r
r
r
s
gs
g
g
g
p
sg
g
r
g
ps
p
p
gs
g
g
r
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
= platform.toUpperCase().indexOf(“MAC”) > -1;
final boolean isIEBrowser
= platform.toUpperCase().indexOf(“IE”) > -1;
final boolean wasResized
= resize >0;
If (isMacOs && isIEBrowser && wasInitialized () && was Resized)
{ ….. other code …..}
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; // initial value of acceleration of first force
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;
} // end getDistanceTravelled
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:
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; }
Example:
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
good.
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;
} // end if
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
} // end mouseReleased
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 another
continue loop;
} // end if won
} // end if &&
} // end for
if (bestmove == bestMoveNotFound) { // neither wins, this move will d
bestmove = potentialComputerMove;
} // end if
} // end if &&
} // end for
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
REMEMBER
For each new method that is not ONLY functionally
tested, you will have to write a unit test.
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
Learning objective –
have simple conditionals
which are self documenting
with meaningful names
Refactoring
In our program with explaining variable
names, there are still some conditionals
which are difficult to understand. We
find them in mainly in bestMove().
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()))
Refactoring
• Refactoring has gained much popularity
during the last few years. There a website
that defines all the refactorings at
• http://www.refactoring.com/catalog/index.html
• You will find that some of these will become
commonplace in your code and some you will
have to work hard to accomplish.
Download