Title: Refactoring
1Refactoring
2Definition
The process of changing a software system in such
a way that it does not alter the external
behavior of the code, yet improves its internal
structure. It is a disciplined way to clean up
code that minimizes the chances of introducing
bugs. Improving the design after its been
written.
3Video Store Example
4public class Movie public static final int
CHILDRENS 2 public static final int REGULAR
0 public static final int NEW_RELEASE
1 public String _title private int
_priceCode public Movie(String title, int
priceCode) _title title _priceCode
priceCode public int getPriceCode()
return _priceCode public void
setPriceCode(int arg) _priceCode arg
public String getTitle() return
_title
5class Rental private Movie _movie private
int _daysRented public Rental(Movie movie,
int daysRented) _movie movie
_daysRented daysRented public int
getDaysRented() return _daysRented
public Movie getMovie() return _movie
6class Customer private String _name
private Vector _rentals new Vector public
Customer(String name) _name name
public void addRental(Rental arg)
_rentals.addElement(arg) public String
getName() return _name
7 public String statement() double
totalAmount 0 int frequentRenterPoints
0 Enumeration rentals _rentals.elements()
String result "Rental Record for "
getName() "\n" while (rentals.hasMoreElem
ents()) double thisAmount 0
Rental each (Rental) rentals.nextElement()
// Determine amounts for each line
switch (each.getMovie().getPriceCode())
case Movie.REGULAR thisAmount 2
if (each.getDaysRented() gt 2)
thisAmount (each.getDaysRented() - 2) 1.5
break case Movie.NEW_RELEASE
thisAmount each.getDaysRented() 3
break case Movie.CHILDRENS
thisAmount 1.5 if
(each.getDaysRented() gt 3) thisAmount
(each.getDaysRented() - 3) 1.5
break
8// Add frequent renter points
frequentRenterPoints // Add bonus for
a two-day, new-release rental if
((each.getMovie().getPriceCode()
Movie.NEW_RELEASE) each.getDaysRented()
gt 1) frequentRenterPoints // Show
figures for this rental result "\t"
each.getMovie().getTitle() "\t"
Sting.valueOf(thisAmount) "\n"
totalAmount thisAmount // Add
footer lines result "Amount owed is "
String.valueOf(totalAmount) "\n" result
"You earned " String.valueOf(frequentRenterPoint
s) " frequent renter points" return
result
9Video Store Example
10Impressions of this code?
Not really OO. Poor organization. Statement
routine supiciously long. What if we had to
change, I.e. put statement in HTML? What if
charging rules change? What if we add new rental
category?
11First step in refactoring
PREPARE TESTS We will look at this in Extreme
Programming And JUnit. Always refactor in small
steps and test.
12Principles
- Abstraction (Information Hiding)
- Flexibility
- Clarity
- Irredundancy
13Refactoring 1 Extract method.
private double amountFor(Rental each)
double thisAmount 0 switch
(each.getMovie().getPriceCode()) case
Movie.REGULAR thisAmount 2
if (each.getDaysRented() gt 2)
thisAmount (each.getDaysRented() - 2) 1.5
break case Movie.NEW_RELEASE
thisAmount each.getDaysRented() 3
break case Movie.CHILDRENS
thisAmount 1.5 if (each.getDaysRented(
) gt 3) thisAmount (each.getDaysRented
() - 3) 1.5 break return
thisAmount
switch (each.getMovie().getPriceCode())
case Movie.REGULAR thisAmount 2
if (each.getDaysRented() gt 2)
thisAmount (each.getDaysRented() - 2) 1.5
break case Movie.NEW_RELEASE
thisAmount each.getDaysRented() 3
break case Movie.CHILDRENS
thisAmount 1.5 if
(each.getDaysRented() gt 3) thisAmount
(each.getDaysRented() - 3) 1.5
break
thisAmount amountFor(each)
14Refactoring 2 Renaming Variables
private double amountFor(Rental aRental)
double result 0 switch (aRental.getMovie().
getPriceCode()) case Movie.REGULAR
result 2 if (aRental.getDaysRented() gt
2) result (aRental.getDaysRented() -
2) 1.5 break case
Movie.NEW_RELEASE result
aRental.getDaysRented() 3 break
case Movie.CHILDRENS result 1.5
if (aRental.getDaysRented() gt 3) result
(aRental.getDaysRented() - 3) 1.5
break return result
15Refactoring 3 Move Method
//add to rental class double getCharge()
double result 0 switch
(getMovie().getPriceCode()) case
Movie.REGULAR result 2 if
(getDaysRented() gt 2) result
(getDaysRented() - 2) 1.5 break
case Movie.NEW_RELEASE result
getDaysRented() 3 break case
Movie.CHILDRENS result 1.5
if (getDaysRented() gt 3) result
(getDaysRented() - 3) 1.5 break
return result
//and adjust customer class private double
amountFor(Rental aRental) return
aRental.getCharge()
16Refactoring 3a. Cleanup Move method
Rental each (Rental) rentals.nextElement()
thisAmount each.getCharge()
And we get rid of amountFor method.
17Refactoring 4 Replace temp with query
while (rentals.hasMoreElements()) double
thisAmount 0 Rental each (Rental)
rentals.nextElement() thisAmount
each.getCharge() // Add frequent renter
points frequentRenterPoints
// Add bonus for a two-day, new-release rental
if ((each.getMovie().getPriceCode()
Movie.NEW_RELEASE) each.getDaysRented()
gt 1) frequentRenterPoints //
Show figures for this rental result "\t"
each.getMovie().getTitle() "\t"
Sting.valueOf(each.getCharge()) "\n"
totalAmount each.getCharge()
thisAmount each.getCharge() // Add
frequent renter points
frequentRenterPoints // Add bonus for
a two-day, new-release rental if
((each.getMovie().getPriceCode()
Movie.NEW_RELEASE) each.getDaysRented()
gt 1) frequentRenterPoints //
Show figures for this rental result "\t"
each.getMovie().getTitle() "\t"
Sting.valueOf(thisAmount) "\n"
totalAmount thisAmount
18Refactoring 5 Extract Method
// Add frequent renter points
frequentRenterPoints // Add bonus for a
two-day, new-release rental if
((each.getMovie().getPriceCode()
Movie.NEW_RELEASE) each.getDaysRented()
gt 1) frequentRenterPoints
frequenRenterPoints each.getFrequentRenterPoint
s()
//add to rental class int getFrequentRenterPoints
() if ((getMovie().getPriceCode()
Movie.NEW_RELEASE) getDaysRented() gt
1) return 2 else return 1
19Now get rid of hard-coded types
- Replace type code with State/Strategy
- Self-Encapsulate Field
- Move Method
- Replace conditional with Polymorphism
20When to refactor
- Rule of Three First time just do it. Second
time, wince at duplication, Third time, refactor. - When you add functionality.
- When you need to fix a bug.
- As you do code review. (Active Reviews)
21Refactoring Problems
- Need more experience and study, but
- Databases
- Changing Interfaces
- When you need to toss and start over
- Performance
22Bad Smells in code
- Duplicated Code (Extract Method)
- Long Method (Extract Method)
- Large Class (Extract Class)
- Long Parameter List (Remove Parameter)
- Divergent Change (Extract Class)
- Shotgun Surgery (Move Method, Move Field)
- Feature Envy (Move/Extract Method)
23More Bad Smells
- Data Clumps (Introduce Parameter Object)
- Primitive Obsession
- Switch Statements
- Parallel Inheritance Hierarchies
- Lazy Class
- Speculative Generality
24Even More Bad Smells
- Temporary Field
- Message Chains
- Middle Man
- Inappropriate Intimacy
- Alternative Classes with different interfaces
- Incomplete library class
- Data class
- Refused Bequest
- Comments
25Tool Support
- Smalltalk Refactoring Browser
- IntelliJ (Commercial)
- Eclipse
- Others on web site
26Next Time
How do we test this stuff?? How do we fit this
into a development strategy? JUnit and Agile
Development