Refactoring Refactoring, applied - Purdue Universityprogram’s code is not structured in a...
Transcript of Refactoring Refactoring, applied - Purdue Universityprogram’s code is not structured in a...
1
C
Refactoring
Martin Fowler (and Kent Beck, John Brant, William Opdyke, Don Roberts), Refactoring- Improving the Design of Existing Code Addison Wesley 1999
CS510 S o f t w
a r e E n g i n e e r
Code, Addison Wesley, 1999.
Refactoring (noun): a change made to the internal structure of software to make it
easier to understand and
1
r i n g
January 20, 2008
cheaper to modify without changing its observable behavior.
Refactor (verb): to restructure software by applying a series of refactorings.
C
Refactoring, applied
Straight from the book:
CS510 S o f t w
a r e E n g i n e e r
“a program to calculate and print a statement of a customer’s charges at a video store”
...price depends on how long the movie is rented and the category of the movie
2
r i n g
January 20, 2008
...also compute frequent renter points
C
Refactoring: Movie
Class diagram of the starting point classes.
CS510 S o f t w
a r e E n g i n e e r
*
1
3
r i n g
January 20, 2008
O b j e c t
Refactoring: Movie Classpublic class MovieMovie {
public static final int CHILDREN=2;public static final int REGULARS=0;public static final int
public void setPriceCode(int arg) {_priceCode = arg;
}
O r i e n t e d S o f t w
a r e E n g
public static final int NEW_RELEASE=1;
private String _title;private int _priceCode;
public Movie(String title, int priceCode) {
_title=title;priceCode = priceCode;
public String getTitle() {return _title;
}}
January 20, 2008 4
g i n e e r i n g
_p p}public int getPriceCode() {
return _priceCode;}
2
O b j e c t
Refactoring: Rental Classpublic class RentalRental {
private Movie _movie;private int _daysRented;
O r i e n t e d S o f t w
a r e E n g
public Rental(Movie movie, int daysRented) {
_movie = movie;_daysRented = daysRented ;
}public int getDaysRented() {
return _daysRented ;}public Movie getMovie() {
t i
January 20, 2008 5
g i n e e r i n g
return _movie;}
}
O b j e c t
Refactoring: Customer Classpublic class CustomerCustomer {
private String _name;private Vector _rentals =new Vector();
O r i e n t e d S o f t w
a r e E n g
public Customer(String name) {_name = name;
}public void addRental(Rental arg) {
_rentals.addElement(arg);}public String getName() {
return _name;}
January 20, 2008 6
g i n e e r i n g
O b j e c t
Refactoring: Customer Classpublic class CustomerCustomer...
public String statement() {double totalAmount = 0; O
r i e n t e d S o f t w a r e E
n g
double totalAmount = 0;int frequentRenterPoints = 0;Enumeration rentals = _rental.elements();String result = “Rental Record for “ + getName() + “\n”;while (rentals.hasMoreElements()) {
double thisAmount = 0;Rental each = (Rental) rentals.nextElement();
// determine amounts for each lineswitch (each.getMovie().getPriceCode()) {
January 20, 2008 7
g i n e e r i n g
switch (each.getMovie().getPriceCode()) {case Movie.REGULAR:
thisAmount += 2;if (each.getDaysRented() > 2)
thisAmount+=(each.getDaysRented()-2) * 1.5;
break;
O b j e c t
Refactoring: Customer Classpublic class CustomerCustomer
public String statement() ...
case Movie NEW RELEASE: O r i e n t e d S o f t w
a r e E n g
case Movie.NEW_RELEASE:thisAmount += each.getDaysRented() * 3;break;
case Movie.CHILDRENS:thisAmount += 1.5;if (each.getDaysRented() > 3)
thisAmount+=(each.getDaysRented()-3) * 1.5;
break;}
January 20, 2008 8
g i n e e r i n g
// add frequent renter pointsfrequentRenterPoints ++;// add bonus for a two day new release rentalif ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE)&&
each.getDaysRented() > 1) frequentRenterPoints++;
3
O b j e c t
Refactoring: Customer Classpublic class CustomerCustomer
public String statement() ...
//show figures for this rental O r i e n t e d S o f t w
a r e E n g
// gresult += “\t” + each.getMovie().getTitle()+ “\t” +
String.valueOf(thisAmount) + “\n”;totalAmount += thisAmount;
}// add footer linesresult += “Amount owed is “+Sting.valueOf(totalAmount) + “\n”;result += “You earned “+Sting.valueOf(frequentRenterPoints)
+ “frequent renter points\n”;return result;
}
January 20, 2008 9
g i n e e r i n g
}
C
Refactoring
Interaction diagram for the statement method.
CS510 S o f t w
a r e E n g i n e e r
10
r i n g
January 20, 2008
C
Refactoring: problem statement
CS510 S o f t w
a r e E n g i n e e r
Add a htmlStatment method which returns a customer statement string containing html tags.
...and there will be some changes to the way movies are classified
affecting frequent renter points and charging
11
r i n g
January 20, 2008
...affecting frequent renter points and charging.
When you find you have to add a feature to a program, and the program’s code is not structured in a convenient way to add it, refactor the code.
CRefactoring: step 1
Write a test suite !
CS510 S o f t w
a r e E n g i n e e r
Refactoring should not affect the outcome of tests. The test suite must exercise the published interface of the classes.
Obviously, refactoring should not affect the published interface. So, avoid publishing interfaces too early.
12
r i n g
January 20, 2008
f , p g f y
4
C
Refactoring: step 2statement() is overly long, apply the Extract Method refactoring
public String statement() {double totalAmount = 0;C
S510 S o f t w a r e E
n g i n e e r
int frequentRenterPoints = 0;Enumeration rentals = _rental.elements();String result = “Rental Record for “ + getName() + “\n”;while (rentals.hasMoreElements()) {
double thisAmount = 0;Rental each = (Rental) rentals.nextElement();// determine amounts for each lineswitch (each.getMovie().getPriceCode()) {
case Movie.REGULAR:thisAmount += 2;if (each.getDaysRented() > 2)
thisAmount+=(each.getDaysRented()-2) * 1.5;b k
13
r i n g
January 20, 2008
break; case Movie.NEW_RELEASE:
thisAmount += each.getDaysRented() * 3; break;case Movie.CHILDRENS:
thisAmount += 1.5;if (each.getDaysRented() > 3)
thisAmount+=(each.getDaysRented()-3) * 1.5;break;
}
C
Refactoring: step 2public String statement() {
double totalAmount = 0;int frequentRenterPoints = 0;Enumeration rentals = _rental.elements();String result = “Rental Record for “ + getName() + “\n”;while (rentals.hasMoreElements()) {
CS510 S o f t w
a r e E n g i n e e r
while (rentals.hasMoreElements()) {double thisAmount = 0;Rental each = (Rental) rentals.nextElement();
thisAmount = amountFor(each);// add frequent renter pointsfrequentRenterPoints ++;// add bonus for a two day new release rentalif ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE)&&
each.getDaysRented() > 1) frequentRenterPoints++;//show figures for this rentalresult += “\t” + each.getMovie().getTitle()+ “\t” +
String.valueOf(thisAmount) + “\n”;
14
r i n g
January 20, 2008
totalAmount += thisAmount;}// add footer linesresult += “Amount owed is “+Sting.valueOf(totalAmount) + “\n”; result += “You
earned “+Sting.valueOf(frequentRenterPoints) + “frequent renter points\n”;
return result;}
C
Refactoring: step 2public public intint amountForamountFor(Rental each) {(Rental each) {
intint thisAmountthisAmount = 0;= 0;switch (each.getMovie().getPriceCode()) {
case Movie.REGULAR:CS510 S o f t w
a r e E n g i n e e r
thisAmount += 2;if (each.getDaysRented() > 2)
thisAmount+=(each.getDaysRented()-2) * 1.5;break;
case Movie.NEW_RELEASE:thisAmount += each.getDaysRented() * 3;break;
case Movie.CHILDRENS:thisAmount += 1.5;if ( h tD R t d() 3)
15
r i n g
January 20, 2008
if (each.getDaysRented() > 3)thisAmount+=(each.getDaysRented()-3) * 1.5;
break;}return return thisAmountthisAmount;;
}}
CRefactoring: step 3
TESTTESTC
S510 S o f t w a r e E
n g i n e e r
16
r i n g
January 20, 2008
5
C
Refactoring: step 4oops, (double) oops, (double) --> (> (intint) bug!) bug!
public doubledouble amountFor(Rental each) {doubledouble thisAmount = 0;switch (each.getMovie().getPriceCode()) {C
S510 S o f t w a r e E
n g i n e e r
( g () g ()) {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.CHILDRENS:thisAmount += 1.5;if ( h tD R t d() > 3)
17
r i n g
January 20, 2008
if (each.getDaysRented() > 3)thisAmount+=(each.getDaysRented()-3) * 1.5;
break;}return thisAmount;
}
C
Refactoring: step 5Variable names not helpful
public double amountFor(Rental each) {double thisAmount = 0;switch (each.getMovie().getPriceCode()) {
CS510 S o f t w
a r e E n g i n e e r
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.CHILDRENS:thi A t + 1 5
18
r i n g
January 20, 2008
thisAmount += 1.5;if (each.getDaysRented() > 3)
thisAmount+=(each.getDaysRented()-3) * 1.5;break;
}return thisAmount;
}
C
Refactoring: step 5public double amountFor(Rental aRentalaRental) {
double resultresult = 0;switch (aRentalaRental.getMovie().getPriceCode()) {
case Movie.REGULAR:CS510 S o f t w
a r e E n g i n e e r
resultresult += 2;if (aRentalaRental.getDaysRented() > 2)
resultresult +=(aRentalaRental.getDaysRented()-2) * 1.5;break;
case Movie.NEW_RELEASE:resultresult += aRentalaRental.getDaysRented() * 3;break;
case Movie.CHILDRENS:resultresult += 1.5;if ( R t lR t l tD R t d() 3)
19
r i n g
January 20, 2008
if (aRentalaRental.getDaysRented() > 3)resultresult +=(aRentalaRental.getDaysRented()-3) * 1.5;
break;}return resultresult ;
}
CRefactoring: step 6
Moving amount computation (does not use info from Customer only Rental)class Customer ...public double amountFor(Rental aRentalaRental) {
double resultresult = 0;CS510 S o f t w
a r e E n g i n e e r
switch (aRentalaRental.getMovie().getPriceCode()) {case Movie.REGULAR:
resultresult += 2;if (aRentalaRental.getDaysRented() > 2)
resultresult +=(aRentalaRental.getDaysRented()-2) * 1.5;break;
case Movie.NEW_RELEASE:resultresult += aRentalaRental.getDaysRented() * 3;break;
case Movie.CHILDRENS:resultresult += 1 5;
20
r i n g
January 20, 2008
resultresult += 1.5;if (aRentalaRental.getDaysRented() > 3)
resultresult +=(aRentalaRental.getDaysRented()-3) * 1.5;break;
}return resultresult ;
}
6
C
Refactoring: step 6class Rental class Rental ......public double getCharge() {
double resultresult = 0;switch (getMovie().getPriceCode()) {
case Mo ie REGULAR
CS510 S o f t w
a r e E n g i n e e r
case Movie.REGULAR:resultresult += 2;
if (getDaysRented() > 2)resultresult +=(getDaysRented()-2) * 1.5;
break; case Movie.NEW_RELEASE:
resultresult += getDaysRented() * 3;break;
case Movie.CHILDRENS:
resultresult += 1.5;
21
r i n g
January 20, 2008
if (getDaysRented() > 3)
resultresult +=(getDaysRented()-3) * 1.5;break;
}return resultresult ;
}
C
Refactoring: step 6class Customer ...class Customer ...public double amountFor(Rental aRental) {
return aRental.getChargeaRental.getCharge()();;}C
S510 S o f t w a r e E
n g i n e e r
22
r i n g
January 20, 2008
C
Refactoring: step 7class Customer ...public String statement() {
double totalAmount = 0;int frequentRenterPoints = 0;Enumeration rentals = _rental.elements();String result = “Rental Record for “ + getName() + “\n”;
CS510 S o f t w
a r e E n g i n e e r
String result = Rental Record for + getName() + \n ;while (rentals.hasMoreElements()) {
double thisAmount = 0;Rental each = (Rental) rentals.nextElement();thisAmount = amountFor(each);// add frequent renter pointsfrequentRenterPoints ++;// add bonus for a two day new release rentalif ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE)&&
each.getDaysRented() > 1) frequentRenterPoints++;//show figures for this rentalresult += “\t” + each.getMovie().getTitle()+ “\t” +
String.valueOf(thisAmount) + “\n”;
23
r i n g
January 20, 2008
totalAmount += thisAmount;}// add footer linesresult += “Amount owed is “+Sting.valueOf(totalAmount) + “\n”; result += “You
earned “+Sting.valueOf(frequentRenterPoints) + “frequent renter points\n”;
return result;}
CRefactoring: step 7
class Customer ...public String statement() {
double totalAmount = 0;int frequentRenterPoints = 0;Enumeration rentals = _rental.elements();String result = “Rental Record for “ + getName() + “\n”;
CS510 S o f t w
a r e E n g i n e e r
String result = Rental Record for + getName() + \n ;while (rentals.hasMoreElements()) {
double thisAmount = 0;Rental each = (Rental) rentals.nextElement();thisAmount = each.getCharge();// add frequent renter pointsfrequentRenterPoints ++;// add bonus for a two day new release rentalif ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE)&&
each.getDaysRented() > 1) frequentRenterPoints++;//show figures for this rentalresult += “\t” + each.getMovie().getTitle()+ “\t” +
String.valueOf(thisAmount) + “\n”;
24
r i n g
January 20, 2008
totalAmount += thisAmount;}// add footer linesresult += “Amount owed is “+Sting.valueOf(totalAmount) + “\n”; result += “You
earned “+Sting.valueOf(frequentRenterPoints) + “frequent renter points\n”;
return result;}
7
C
Refactoring
State of classes after moving the charge method. amountFor has been deleted.
CS510 S o f t w
a r e E n g i n e e r
*
1
25
r i n g
January 20, 2008
C
Refactoring: step 8Replace Temp with Query (thisAmount is redundant)
class Customer ...class Customer ...public String statement() {
double totalAmount = 0;int frequentRenterPoints = 0;
CS510 S o f t w
a r e E n g i n e e r
int frequentRenterPoints = 0;Enumeration rentals = _rental.elements();String result = “Rental Record for “ + getName() + “\n”;while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();// add frequent renter pointsfrequentRenterPoints ++;// add bonus for a two day new release rentalif ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE)&&
each.getDaysRented() > 1) frequentRenterPoints++;//show figures for this rentalresult += “\t” + each.getMovie().getTitle()+ “\t” +
)) + “\n”;
26
r i n g
January 20, 2008
totalAmount += each.getCharge(); String.valueOf(each.getCharge(}// add footer linesresult += “Amount owed is “+Sting.valueOf(totalAmount) + “\n”; result += “You
earned “+Sting.valueOf(frequentRenterPoints) + “frequent renter points\n”;
return result;}
C
Refactoring: step 9Extract Method (frequent renter computation)
class Customer ...class Customer ...public String statement() {
double totalAmount = 0;int frequentRenterPoints = 0;
CS510 S o f t w
a r e E n g i n e e r
int frequentRenterPoints = 0;Enumeration rentals = _rental.elements();String result = “Rental Record for “ + getName() + “\n”;while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();// add frequent renter points// add frequent renter pointsfrequentRenterPointsfrequentRenterPoints ++;++;// add bonus for a two day new release rental// add bonus for a two day new release rentalif ((if ((each.getMovieeach.getMovie().().getPriceCodegetPriceCode() == () == Movie.NEW_RELEASEMovie.NEW_RELEASE)&&)&&
each.getDaysRentedeach.getDaysRented() > 1) () > 1) frequentRenterPointsfrequentRenterPoints++;++;//show figures for this rentalresult += “\t” + each.getMovie().getTitle()+ “\t” +
String.valueOf(each.getCharge()) + “\n”;
27
r i n g
January 20, 2008
totalAmount += each.getCharge();}// add footer linesresult += “Amount owed is “+Sting.valueOf(totalAmount) + “\n”; result += “You
earned “+Sting.valueOf(frequentRenterPoints) + “frequent renter points\n”;
return result;}
CRefactoring: step 9
class Customer ...class Customer ...public String statement() {
double totalAmount = 0;int frequentRenterPoints = 0;Enumeration rentals = _rental.elements();C
S510 S o f t w a r e E
n g i n e e r
String result = “Rental Record for “ + getName() + “\n”;while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();frequentRenterPointsfrequentRenterPoints += += each.getFrequentRenterPointseach.getFrequentRenterPoints();();
//show figures for this rentalresult += “\t” + each.getMovie().getTitle()+ “\t” +
String.valueOf(each.getCharge()) + “\n”;totalAmount += each.getCharge();
}// add footer linesresult + “Amount owed is “+Sting valueOf(totalAmount) + “\n”; result + “You
28
r i n g
January 20, 2008
result += “Amount owed is “+Sting.valueOf(totalAmount) + “\n”; result += “You earned “+Sting.valueOf(frequentRenterPoints)
+ “frequent renter points\n”;return result;
}
8
C
Refactoring: step 9class Rental ...class Rental ...public int getFrequentRenterPoints() {
if ((if ((getMoviegetMovie().().getPriceCodegetPriceCode() == () == Movie.NEW_RELEASEMovie.NEW_RELEASE) ) && && getDaysRentedgetDaysRented() > 1) () > 1) C
S510 S o f t w a r e E
n g i n e e r
g yg yreturn 2;return 2;
else else return 1;return 1;
}}
29
r i n g
January 20, 2008
C
RefactoringClass diagram before extraction and movement of the frequent renter points calculation
*
1
CS510 S o f t w
a r e E n g i n e e r
Interaction diagram before extraction and movement of the frequent renter points calculation
1
30
r i n g
January 20, 2008
C
RefactoringClass diagram after extraction and movement of the frequent renter points calculation
*
1
CS510 S o f t w
a r e E n g i n e e r
Interaction diagram after extraction and movement of the frequent renter points calculation
1
31
r i n g
January 20, 2008
getFrequentRenterPoints()
CRefactoring: step 10
Replace Temp with Query (the temporaries make the method complex and force code duplication)
class Customer ...public String statement() {C
S510 S o f t w a r e E
n g i n e e r
double totalAmount = 0;int frequentRenterPoints = 0;Enumeration rentals = _rental.elements();String result = “Rental Record for “ + getName() + “\n”;while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();frequentRenterPoints += each.getFrequentRenterPoints();
//show figures for this rentalresult += “\t” + each.getMovie().getTitle()+ “\t” +
String.valueOf(each.getCharge()) + “\n”;totalAmount + each getCharge();
32
r i n g
January 20, 2008
totalAmount += each.getCharge();}// add footer linesresult += “Amount owed is “+Sting.valueOf(totalAmount) + “\n”; result += “You
earned “+Sting.valueOf(frequentRenterPoints) + “frequent renter points\n”;
return result;}
9
C
Refactoring: step 10class Customer ...class Customer ...public String statement() {
int frequentRenterPoints = 0;Enumeration rentals = _rental.elements();C
S510 S o f t w a r e E
n g i n e e r
String result = “Rental Record for “ + getName() + “\n”;while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();frequentRenterPoints += each.getFrequentRenterPoints();//show figures for this rentalresult += “\t” + each.getMovie().getTitle()+ “\t” +
String.valueOf(each.getCharge()) + “\n”;}// add footer lines
lt “A t d i “ Sti l Of( tT t lChtT t lCh ()()) “\ ”
33
r i n g
January 20, 2008 33
result += “Amount owed is “+Sting.valueOf(getTotalChargegetTotalCharge()()) + “\n”;result += “You earned
“+Sting.valueOf(frequentRenterPoints)+“frequent renter points\n”;
return result;}
C
Refactoring: step 10class Customer ...class Customer ...private double getTotalCharge() {
double result = 0;Enumeration rentals = rentals.elements();C
S510 S o f t w a r e E
n g i n e e r
_while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();result += each.getCharge();
}return result;
}
34
r i n g
January 20, 2008
C
Refactoring: step 11Replace Temp with Query
class Customer ...class Customer ...public String statement() {
int frequentRenterPoints = 0;
CS510 S o f t w
a r e E n g i n e e r
int frequentRenterPoints 0;Enumeration rentals = _rental.elements();String result = “Rental Record for “ + getName() + “\n”;while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();frequentRenterPoints += each.getFrequentRenterPoints();//show figures for this rentalresult += “\t” + each.getMovie().getTitle()+ “\t” +
String.valueOf(each.getCharge()) + “\n”;}// dd f t li
35
r i n g
January 20, 2008
// add footer linesresult += “Amount owed is “+Sting.valueOf(getTotalCharge()) + “\n”;result += “You earned “+Sting.valueOf(frequentRenterPoints)+
“frequent renter points\n”;return result;
}
CRefactoring: step 11
Replace Temp with Queryclass Customer ...class Customer ...public String statement() {
Enumeration rentals = rental.elements();CS510 S o f t w
a r e E n g i n e e r
u e at o e ta s _ e ta .e e e ts();String result = “Rental Record for “ + getName() + “\n”;while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();//show figures for this rentalresult += “\t” + each.getMovie().getTitle()+ “\t” +
String.valueOf(each.getCharge()) + “\n”;}// add footer linesresult += “Amount owed is “+Sting.valueOf(getTotalCharge()) + “\n”;
36
r i n g
January 20, 2008
g (g g ())
result += “You earned “+Sting.valueOf(getFrequentRenterPointsgetFrequentRenterPoints()())+“frequent renter points\n”;
return result;}
10
C
Refactoring: step 11class Customer ...class Customer ...
private double getFrequentRenterPoints() {double result = 0;Enumeration rentals = rentals.elements();C
S510 S o f t w a r e E
n g i n e e r
_while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();result += each.getFrequentRenterPoints();
}return result;
}
37
r i n g
January 20, 2008
C
RefactoringClass diagram before extraction of the totals
*
1
CS510 S o f t w
a r e E n g i n e e r
Interaction diagram before extraction of the totals
1
38
r i n g
January 20, 2008
getFrequentRenterPoints()
C
RefactoringClass diagram after extraction of the totals
*
1
CS510 S o f t w
a r e E n g i n e e r
Interaction diagram after extraction of the totals
1
39
r i n g
January 20, 2008
CRefactoring
RemarksMost refactoring reduce code size, but this is not necessarily the case The point is to make code easier to modify and more
CS510 S o f t w
a r e E n g i n e e r
the case. The point is to make code easier to modify and more readable.Performance gets a hit by running the same loop three times, or does it? Profile the program and find the answer.
40
r i n g
January 20, 2008
11
C
Software extensionThe requested method can be added with minimal code duplication
l C t
CS510 S o f t w
a r e E n g i n e e r
class Customer ...public String htmlStatement() {
Enumeration rentals = _rental.elements();String result = “<H1>Rental Record for<EM> “ + getName() + “<EM></H1><P>\n”;while (rentals.hasMoreElements()) {
Rental each = (Rental) rentals.nextElement();//show figures for this rentalresult += each.getMovie().getTitle()+ “: ” +
String.valueOf(each.getCharge()) + “<BR>\n”;}// add footer lines
41
r i n g
January 20, 2008
// add footer linesresult += “<P>Amount owed is<EM> “+Sting.valueOf(getTotalCharge()) +
“</EM><P>\n”; result += “You earned <EM>“+Sting.valueOf(getFrequentRenterPointsgetFrequentRenterPoints()())+
“</EM> frequent renter points<P>\n”;return result;
}
C
New functionality
Getting ready to change the classification of the movies in the store. P h l f h d f
CS510 S o f t w
a r e E n g i n e e r
Perhaps new classification, perhaps modification to existing.Charging and frequent renting will be affected.
42
r i n g
January 20, 2008
C
Refactoring: step 12Replacing conditional logic on Price Code with polymorphism
CS510 S o f t w
a r e E n g i n e e r
43
r i n g
January 20, 2008
CRefactoring: step 12
Move getChargeclass Rental ...class Rental ...public double getCharge() {
double result = 0;CS510 S o f t w
a r e E n g i n e e r
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.CHILDRENS:result += 1 5;
44
r i n g
January 20, 2008
result += 1.5;if (getDaysRented() > 3)
result +=(getDaysRented()-3) * 1.5;break;
}return result ;
}
12
C
Refactoring: step 12class Movie ...class Movie ...public double getCharge(int daysRented) {
double result = 0;switch (getPriceCode()) {
case REGULAR
CS510 S o f t w
a r e E n g i n e e r
case REGULAR:result += 2;if (daysRented > 2)
result +=(daysRented-2) * 1.5;break;
case NEW_RELEASE:result += daysRented * 3;break;
case CHILDRENS:result += 1.5;
45
r i n g
January 20, 2008
if (daysRented > 3)result +=(daysRented-3) * 1.5;
break;}return result ;
}
C
Refactoring: step 12class Rental ...class Rental ...public double getCharge() {
return _movie.getCharge(_daysRented);}C
S510 S o f t w a r e E
n g i n e e r
46
r i n g
January 20, 2008
C
Refactoring: step 13Move getFrequentRenterPoints()
class Rental ...class Rental ...public int getFrequentRenterPoints() {
CS510 S o f t w
a r e E n g i n e e r
public int getFrequentRenterPoints() {if ((getMovie().getPriceCode() == Movie.NEW_RELEASE)&& getDaysRented() > 1)
return 2;else
return 1;}
47
r i n g
January 20, 2008
CRefactoring: step 13
class Movie ...public int getFrequentRenterPointsgetFrequentRenterPoints((intint daysRenteddaysRented) ) {
if ((getPriceCode() == Movie.NEW_RELEASE) && daysRented > 1) return 2;C
S510 S o f t w a r e E
n g i n e e r
else return 1;
}
class Rental ...public int getFrequentRenterPoints() {
return _movie.getFrequentRenterPoints(_daysRented);
48
r i n g
January 20, 2008
}
13
C
Refactoring
Class diagram before moving methods to movie
CS510 S o f t w
a r e E n g i n e e r
Class diagram after moving methods to movie
*
1
49
r i n g
January 20, 2008
*
1
C
Refactoring
Inheritance
CS510 S o f t w
a r e E n g i n e e r
50
r i n g
January 20, 2008
C
RefactoringInheritance
CS510 S o f t w
a r e E n g i n e e r
51
r i n g
January 20, 2008
CRefactoring: step 14
Replace Type Code with State/StrategyC
S510 S o f t w a r e E
n g i n e e r
class Movie ...class Movie ...public Movie(String name, int priceCode) {
_name = name;_priceCode = priceCode;
}
52
r i n g
January 20, 2008
14
C
Refactoring: step 14
class Movie ...class Movie ...public Movie(String name, int priceCode) {
_name = name;CS510 S o f t w
a r e E n g i n e e r
setPriceCode(priceCode);setPriceCode(priceCode);}
53
r i n g
January 20, 2008 53
C
Refactoring: step 14abstract class Price {
abstract int getPriceCode();}
CS510 S o f t w
a r e E n g i n e e r
class ChildrenPrice extends Price {int getPriceCode(){
return MOVIE.CHILDREN;}
}class NewReleasePrice extends Price {
int getPriceCode(){return MOVIE.NEW_RELEASE;
}}
54
r i n g
January 20, 2008
}class RegularPrice extends Price {
int getPriceCode(){return MOVIE.REGULAR;
}}
C
Refactoring: step 15class Movie ...class Movie ...
public int getPriceCode() {return priceCode;C
S510 S o f t w a r e E
n g i n e e r
_p}public void setPriceCode(int arg) {
_priceCode = arg;}private int _priceCode;
55
r i n g
January 20, 2008
CRefactoring: step 15
class Movie ...class Movie ...public int getPriceCode() {
return _return _price.getPriceCodeprice.getPriceCode;;}p blic oid setPriceCode(int arg) {
CS510 S o f t w
a r e E n g i n e e r
public void setPriceCode(int arg) {switch (switch (argarg) {) {
case REGULAR:case REGULAR:_price = new _price = new RegularPriceRegularPrice();();break;break;
case CHILDREN:case CHILDREN:_price = new _price = new ChildrenPriceChildrenPrice();();break;break;
case NEW_RELEASE:case NEW_RELEASE:_price = new _price = new NewReleasePriceNewReleasePrice();();
56
r i n g
January 20, 2008
break;break;default: default: throw new throw new IllegalArgumentExceptionIllegalArgumentException(“Incorrect Price Code”);(“Incorrect Price Code”);
}}}private Price _price;private Price _price;
15
C
Refactoring: step 16Move Method
class Movie ...class Movie ...public double getCharge(int daysRented) {
double result = 0;CS510 S o f t w
a r e E n g i n e e r
switch (getPriceCode()) {case REGULAR:
result += 2;if (getDaysRented() > 2)
result +=(getDaysRented()-2) * 1.5;break;
case NEW_RELEASE:result += getDaysRented() * 3;break;
case CHILDRENS:result += 1 5;
57
r i n g
January 20, 2008
result + 1.5;if (getDaysRented() > 3)
result +=(getDaysRented()-3) * 1.5;break;
}return result ;
}
C
Refactoring: step 16class Movie ...class Movie ...public double getCharge(int daysRented) {
return __price.getChargeprice.getCharge((daysRenteddaysRented));}C
S510 S o f t w a r e E
n g i n e e r
58
r i n g
January 20, 2008
C
Refactoring: step 16Replace Conditional with Polymorphism
class Price ...class Price ...double getCharge(int daysRented) {
double result = 0;CS510 S o f t w
a r e E n g i n e e r
switch (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.CHILDRENS:result += 1 5;
59
r i n g
January 20, 2008
result += 1.5;if (getDaysRented() > 3)
result +=(getDaysRented()-3) * 1.5;break;
}return result ;
}
CRefactoring: step 16
class RegularPrice ...class RegularPrice ...double getCharge(int daysRented) {
double result = 2;if (getDaysRented() > 2)res lt + (getDa sRented() 2) * 1 5
CS510 S o f t w
a r e E n g i n e e r
result +=(getDaysRented()-2) * 1.5;return result;
}class NewReleasePrice ...class NewReleasePrice ...double getCharge(int daysRented) {
return daysRented * 3;}class ChildrenPrice ...class ChildrenPrice ...double getCharge(int daysRented) {
double result = 1.5;
60
r i n g
January 20, 2008
if (getDaysRented() > 3)result +=(getDaysRented()-3) * 1.5;
return result ;}class Price...class Price...
abstract double getCharge(int daysRented);
16
C
Refactoring: step 17Replace Conditional with Polymorphism
class Rental ...class Rental ...int getFrequentRenterPoints(int daysRented) {
CS510 S o f t w
a r e E n g i n e e r
int getFrequentRenterPoints(int daysRented) {if ((getPriceCode() == Movie.NEW_RELEASE) && daysRented > 1)
return 2;else
return 1;}
61
r i n g
January 20, 2008
C
Refactoring: step 17class Movie ...class Movie ...int getFrequentRenterPoints(int daysRented) {
return _price.getFrequentRenterPoints(daysRented);}C
S510 S o f t w a r e E
n g i n e e r
class Price...class Price...int getFrequentRenterPoints(int daysRented) {
return 1;}
class class NewReleasePriceNewReleasePrice....int getFrequentRenterPoints(int daysRented) {
return (daysRented > 1) ? 2:1;
62
r i n g
January 20, 2008
}
C
Refactoring Principles
Why do we Why do we refactorrefactor??To improve the design of softwareTo make software easier to understandC
S510 S o f t w a r e E
n g i n e e r
To help you find bugsTo make you program faster
When should we When should we refactorrefactor??1. Refactor when you add functionality2. Refactor when you need to fix a bug3. Refactor as you do code reviews– Refactor when the code starts to smell.
What about performance?What about performance?
63
r i n g
January 20, 2008
What about performance?What about performance?Worry about performance only when you have identified a performance problem
CBad Smells in Code
If it stinks, change it.--Grandma Beck on child rearing
CS510 S o f t w
a r e E n g i n e e r
Duplicated Code (stench 10)
If the same code structure is repeated
Extract Method- gather duplicated codePull Up Field - move to a common parentF T l t M th d th si il ts l i h l s
64
r i n g
January 20, 2008
Form Template Method - gather similar parts, leaving holesSubstitute Algorithm - choose the clearer algorithmExtract class - for unrelated classes, create a new class with functionality
17
C
Bad Smells in Code
Long Method(stench 7)
CS510 S o f t w
a r e E n g i n e e r
If the body of a method is over a page (choose your page size)
Extract Method- extract related behaviorReplace Temp with Query - remove temporaries when they obscure meaningIntroduce Parameter Object - slim down parameter lists by
ki h i bj
65
r i n g
January 20, 2008
making them into objectsReplace Method with Method Object - still too many parametersDecompose Conditionals - conditional and loops can be moved to their own methods
C
Bad Smells in Code
Large Class (stench 7)
If l h ith t i bl t th d
CS510 S o f t w
a r e E n g i n e e r
If a class has either too many variables or too many methods
Extract Class - to bundle variables/methods
66
r i n g
January 20, 2008
C
Bad Smells in Code
Divergent Change (stench 5)
If fi d lf t dl h i th l th
CS510 S o f t w
a r e E n g i n e e r
If you find yourself repeatedly changing the same class thenthere is probably something wrong with it
Extract Class - group functionality commonly changed into a class
67
r i n g
January 20, 2008
CBad Smells in Code
Shotgun Surgery (stench 5)
If fi d lf ki l t f ll h f h
CS510 S o f t w
a r e E n g i n e e r
If you find yourself making a lot of small changes for each desired
change
Move Method/Field - pull all the changes into a single classInline Class - group a bunch of behaviors together
68
r i n g
January 20, 2008
18
C
Bad Smells in Code
Feature Envy(stench 6)
CS510 S o f t w
a r e E n g i n e e r
If a method seems more interested in a class other than the class it
actually is in
Move Method - move the method to the desired classExtract Method - if only part of the method shows the
69
r i n g
January 20, 2008
symptoms
C
Bad Smells in Code
Data Clumps (stench 4)
D t it th t f tl t th i th d i t
CS510 S o f t w
a r e E n g i n e e r
Data items that are frequently together in method signatures and
classes belong to a class of their own
Extract Class - turn related fields into a classIntroduce Parameter Object - for method signatures
70
r i n g
January 20, 2008
C
Bad Smells in Code
Primitive Obsession (stench 3)
CS510 S o f t w
a r e E n g i n e e r
Primitive types inhibit change
Replace Data Value with Object - on individual data valuesMove Method/Field - pull all the changes into a single classIntroduce Parameter Object - for signaturesReplace Array with Object - to get rid of arrays
71
r i n g
January 20, 2008
CBad Smells in Code
Switch Statements(stench 5)
CS510 S o f t w
a r e E n g i n e e r
Switch statements lead to duplication and inhibit change
Extract method - to remove the switchMove method - to get the method where polymorphism can applyReplace Type Code with State/Strategy - set up inheritance
72
r i n g
January 20, 2008
Replace Conditional with Polymorphism - get rid of the switch
19
C
Bad Smells in Code
Parallel Inheritance Hierarchies (stench 6)
If h k b l i f th hi h
CS510 S o f t w
a r e E n g i n e e r
If when ever you make a subclass in one corner of the hierarchy, you must create another subclass in another corner
Move Method/Field - get one hierarchy to refer to the other
73
r i n g
January 20, 2008
C
Bad Smells in Code
Lazy Class (stench 4)
If l ( ft f t i ) d t d h li i t it
CS510 S o f t w
a r e E n g i n e e r
If a class (e.g. after refactoring) does not do much, eliminate it.
Collapse Hierarchy- for subclassesInline Class - remove a single class
74
r i n g
January 20, 2008
C
Bad Smells in Code
Speculative Generality(stench 4)
CS510 S o f t w
a r e E n g i n e e r
If a class has features that are only used in test cases, remove them.
Collapse Hierarchy- for useless abstract classesInline Class - for useless delegationRename Method - methods with odd abstract names should be b h d h
75
r i n g
January 20, 2008
brought down to earth
CBad Smells in Code
Temporary Field (stench 3)
If l h fi ld th t l t i i l t t
CS510 S o f t w
a r e E n g i n e e r
If a class has fields that are only set in special cases, extract them
Extract Class- for the special fields
76
r i n g
January 20, 2008
20
C
Bad Smells in Code
Message Chains (stench 3)
L h i f t t t l b ittl
CS510 S o f t w
a r e E n g i n e e r
Long chains of messages to get to a value are brittle as any change
in the intermittent structure will break the code
Hide Delegate - remove one link in a chainExtract Method - change the behavior to avoid chains
77
r i n g
January 20, 2008
C
Bad Smells in Code
Middle Man (stench 3)
A i t di bj t i d t ft t t t
CS510 S o f t w
a r e E n g i n e e r
An intermediary object is used too often to get at encapsulated values
Remove Middle Man - to talk directly to the targetReplace Delegation with Inheritance - turns the middle man into a subclass of the real object
78
r i n g
January 20, 2008
C
Bad Smells in Code
Inappropriate Intimacy (stench 5)
Cl t i ti t d d t h ti d l i i
CS510 S o f t w
a r e E n g i n e e r
Classes are too intimate and spend too much time delving in each other’s private parts
Move Method/Field - to separate pieces in order to reduce intimacyExtract Class - make a common class of shared behavior/dataReplace Inheritance with Delegation - when a subclass is
79
r i n g
January 20, 2008
Replace Inheritance with Delegation when a subclass is getting too cozy
CBad Smells in Code
Comments (stench 2)
C t ft i f l d id
CS510 S o f t w
a r e E n g i n e e r
Comments are often a sign of unclear code... consider refactoring
80
r i n g
January 20, 2008