SlideShare ist ein Scribd-Unternehmen logo
1 von 54
Downloaden Sie, um offline zu lesen
1
CMPUT 301: Lecture 14
Refactoring
Lecturer: Sherif Ghali
Department of Computing Science
University of Alberta
Notes credits:
Ken Wong, Sherif Ghali
(Some slides removed by M. Hicks)
2
Refactoring
Example:
a program to calculate and print a
statement of a customer s charges at a
video store
2
3
Class Diagram
-priceCode : int
Movie
Rental
-daysRented : int
Customer
+statement()
*
1
*
+getDaysRented (): int
+getPriceCode (): int
4
Movie
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;
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;
}
}
3
5
Rental
class 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;
}
}
6
Customer
class 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;
}
}
4
7
Customer::statement()
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();
// 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.CHILDRENS:
thisAmount += 1.5;
if (each.getDaysRented() > 3)
thisAmount += (each.getDaysRented() - 3) * 1.5;
break;
}
8
Customer::statement()
// add frequent renter points
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;
}
}
5
9
Refactoring
:Customer :Rental :Movie
* [for all rentals]
getMovie ()
getPriceCode ()
getDaysRented ()
statement()
10
Refactoring
Something is rotten in the state of
Denmark
What is it?
6
11
Refactoring
Issues:
not object-oriented
statement( ) routine does too much
Customer class is a blob
potentially difficult to make changes
e.g., HTML output
e.g., new charging rules
12
Refactoring
Idea:
If the code is not structured conveniently to
add a feature, first refactor the program to
make it easy to add the feature, then add
the feature.
7
13
Refactoring
First step:
Build self-checking tests.
14
Refactoring
Decompose statement() method:
Extract logical chunk of code as a new
method.
Apply Extract Method.
8
15
class Customer {
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();
// 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.CHILDRENS:
thisAmount += 1.5;
if (each.getDaysRented() > 3)
thisAmount += (each.getDaysRented() - 3) * 1.5;
break;
}
16
Refactoring
class Customer {
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 );
9
17
Refactoring
class Customer {
private double amountFor( Rental each ) {
double thisAmount = 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.CHILDRENS:
thisAmount += 1.5;
if (each.getDaysRented() > 3)
thisAmount += (each.getDaysRented() - 3) * 1.5;
break;
}
return thisAmount;
}
}
18
Refactoring
Compile and test!
small steps
What do we do next?
10
19
Refactoring
Rename variables in amountFor():
Enhance readability.
20
Refactoring
class Customer {
private double amountFor( Rental each ) {
double thisAmount = 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.CHILDRENS:
thisAmount += 1.5;
if (each.getDaysRented() > 3)
thisAmount += (each.getDaysRented() - 3) * 1.5;
break;
}
return thisAmount;
}
}
11
21
Refactoring
class Customer {
private double amountFor( Rental aRental ) {
double result = 0;
switch (aRental.getMovie().getPriceCode()) {
case Movie.REGULAR:
result += 2;
if (aRental.getDaysRented() > 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() > 3)
result += (aRental.getDaysRented() - 3) * 1.5;
break;
}
return result;
}
}
22
Refactoring
Compile and test.
What do we do next?
12
23
Refactoring
Move amountFor() to Rental class:
Method uses rental information, but not
customer information.
Move method to the right class.
Apply Move Method.
24
Refactoring
class Customer {
private double amountFor( Rental aRental ) {
double result = 0;
switch (aRental.getMovie().getPriceCode()) {
case Movie.REGULAR:
result += 2;
if (aRental.getDaysRented() > 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() > 3)
result += (aRental.getDaysRented() - 3) * 1.5;
break;
}
return result;
}
}
13
25
Refactoring
class Rental {
double getCharge() {
double result = 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.CHILDRENS:
result += 1.5;
if (getDaysRented() > 3)
result += (getDaysRented() - 3) * 1.5;
break;
}
return result;
}
}
26
Refactoring
class Customer {
private double amountFor( Rental aRental ) {
return aRental.getCharge();
}
}
14
27
Refactoring
Compile and test.
28
Refactoring
Replace references to amountFor()
with getCharge():
Adjust references to old method to use
new method.
Remove old method.
15
29
Refactoring
class Customer {
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 );
30
Refactoring
class Customer {
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();
16
31
Refactoring
Compile and test.
32
Refactoring
Eliminate thisAmount temporary in
statement():
Replace redundant temporary variable with
query.
Apply Replace Temp with Query.
17
33
Refactoring
class Customer {
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();
// add frequent renter points
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;
}
34
Refactoring
class Customer {
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();
// add frequent renter points
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( each.getCharge() ) + n ;
totalAmount += each.getCharge();
}
18
35
Refactoring
Extract frequent renter points logic:
Applicable rules belong to the rental, not
the customer.
Apply Extract Method and Move Method.
36
Refactoring
class Customer {
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();
// add frequent renter points
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( each.getCharge() ) + n ;
totalAmount += each.getCharge();
}
19
37
Refactoring
class Customer {
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();
// add frequent renter points
frequentRenterPoints += each.getFrequentRenterPoints();
// show figures for this rental
result += t + each.getMovie().getTitle() + t +
String.valueOf( each.getCharge() ) + n ;
totalAmount += each.getCharge();
}
38
Refactoring
class Rental {
int getFrequentRenterPoints() {
if ((getMovie().getPriceCode() == Movie.NEW_RELEASE) &&
getDaysRented() > 1)
return 2;
else
return 1;
}
}
20
39
Refactoring
Eliminate totalAmount temporary:
Apply Replace Temp with Query.
40
Refactoring
class Customer {
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();
// add frequent renter points
frequentRenterPoints += each.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 ) + n ;
result += You earned + String.valueOf( frequentRenterPoints ) +
frequent renter points ;
return result;
}
}
21
41
class Customer {
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();
// add frequent renter points
frequentRenterPoints += each.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 ) +
frequent renter points ;
return result;
}
}
Refactoring
42
class Customer {
private double getTotalCharge() {
double result = 0;
Enumeration rentals = _rentals.elements();
while (rentals.hasMoreElements()) {
Rental each = (Rental)rentals.nextElement();
result += each.getCharge();
}
return result;
}
}
Refactoring
22
43
Refactoring
Eliminate frequentRenterPoints
temporary:
Apply Replace Temp with Query.
44
class Customer {
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();
// add frequent renter points
frequentRenterPoints += each.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 ) +
frequent renter points ;
return result;
}
}
Refactoring
23
45
Refactoring
class Customer {
public String statement() {
Enumeration rentals = _rentals.elements();
String result = Rental Record for + getName() + n ;
while (rentals.hasMoreElements()) {
Rental each = (Rental)rentals.nextElement();
// 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( getTotalFrequentRenterPoints() ) +
frequent renter points ;
return result;
}
}
46
Refactoring
class Customer {
private int getTotalFrequentRenterPoints() {
int result = 0;
Enumeration rentals = _rentals.elements();
while (rentals.hasMoreElements()) {
Rental each = (Rental)rentals.nextElement();
result += each.getFrequentRenterPoints();
}
return result;
}
}
24
47
Refactoring
-priceCode : int
Movie
Rental
-daysRented : int
+getCharge (): double
+getFrequentRenterPoints (): int
Customer
+statement()
-getTotalCharge (): double
-getTotalFrequentRenterPoints (): int
*
1
*
+getPriceCode (): int
+getDaysRented (): int
48
Refactoring
:Customer :Rental :Movie
getTotalCharge ()
getPriceCode ()
statement()
getTotalFrequentRenterPoints ()
* [for all rentals]
getCharge ()
* [for all rentals]
getFrequentRenterPoints () getPriceCode ()
25
49
-priceCode:int
Movie
Rental
-daysRented:int
+getCharge(): double
+getFrequentRenterPoints():int
Customer
+statement()
-getTotalCharge(): double
-getTotalFrequentRenterPoints():int
*
1
*
+getPriceCode():int
+getDaysRented():int
50
Refactoring
Issues:
more code
slower performance?
26
51
Refactoring
New feature (HTML output):
class Customer {
public String htmlStatement() {
Enumeration rentals = _rentals.elements();
String result = <H1>Rental Record for + getName() + </H1>n ;
while (rentals.hasMoreElements()) {
Rental each = (Rental)rentals.nextElement();
// show figures for this rental
result += each.getMovie().getTitle() + : +
String.valueOf( each.getCharge() ) + <BR>n ;
}
// add footer lines
result += <P>Amount owed is +
String.valueOf( getTotalCharge() ) + </P>n ;
result += <P>You earned +
String.valueOf( getTotalFrequentRenterPoints() ) +
frequent renter points</P> ;
return result;
}
}
52
Refactoring
More needs:
new classifications of movies
27
53
Refactoring
class Rental {
double getCharge() {
double result = 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.CHILDRENS:
result += 1.5;
if (getDaysRented() > 3)
result += (getDaysRented() - 3) * 1.5;
break;
}
return result;
}
}
54
Refactoring
Replace conditional logic on price code
with polymorphism:
Rental logic should not depend on specific
movie types.
It is generally bad design to do a switch
on an another object s attribute.
28
55
Refactoring
class Movie {
double getCharge( int daysRented ) {
double result = 0;
switch (getPriceCode()) {
case Movie.REGULAR:
result += 2;
if (daysRented > 2)
result += (daysRented - 2) * 1.5;
break;
case Movie.NEW_RELEASE:
result += daysRented * 3;
break;
case Movie.CHILDRENS:
result += 1.5;
if (daysRented > 3)
result += (daysRented - 3) * 1.5;
break;
}
return result;
}
}
56
Refactoring
class Rental {
double getCharge() {
return _movie.getCharge( _daysRented );
}
}
29
57
Refactoring
class Rental {
int getFrequentRenterPoints() {
if ((getMovie().getPriceCode() == Movie.NEW_RELEASE) &&
getDaysRented() > 1)
return 2;
else
return 1;
}
}
58
Refactoring
class Movie {
int getFrequentRenterPoints( int daysRented ) {
if ((getPriceCode() == Movie.NEW_RELEASE) &&
daysRented > 1)
return 2;
else
return 1;
}
}
30
59
Refactoring
class Rental {
int getFrequentRenterPoints() {
return _movie.getFrequentRenterPoints( _daysRented );
}
}
60
Refactoring
Get rid of the switch statement:
class Movie {
double getCharge( int daysRented ) {
double result = 0;
switch (getPriceCode()) {
case Movie.REGULAR:
result += 2;
if (daysRented > 2)
result += (daysRented - 2) * 1.5;
break;
case Movie.NEW_RELEASE:
result += daysRented * 3;
break;
case Movie.CHILDRENS:
result += 1.5;
if (daysRented > 3)
result += (daysRented - 3) * 1.5;
break;
}
return result;
}
}
31
61
Refactoring
?
-priceCode : int
Movie
+getCharge ( days: int ): double
+getCharge ( days: int ): double +getCharge ( days: int ): double+getCharge ( days: int ): double
Regular Movie Childrens Movie New Release Movie
62
Refactoring
We have two flaws. What are they?
Hint:
Is the movie classification static?
32
63
Refactoring
Flaw:
A movie may change its classification
during its lifetime.
An object cannot change its class during its
lifetime.
64
Refactoring
Solution:
Use State design pattern.
33
65
Refactoring
Price
Regular Price
+getCharge( days: int ): double
Childrens Price New Release Price
Movie
return
price.getCharge ( days )
+getCharge( days: int ): double +getCharge( days: int ): double
+getCharge( days: int ): double
1*
+getCharge( days: int ): double
66
Refactoring
Replace price (type) code:
Apply Replace Type Code with State.
Compile and test after each step.
34
67
Refactoring
Note:
Make sure uses of the price type code go
through accessor methods
68
Refactoring
public class Movie {
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;
}
}
35
69
Refactoring
public class Movie {
private int _priceCode;
public Movie( String title, int priceCode ) {
_title = title;
setPriceCode( priceCode );
}
public int getPriceCode() {
return _priceCode;
}
public void setPriceCode( int arg ) {
_priceCode = arg;
}
}
70
Refactoring
Add new state classes:
abstract class Price {
abstract int getPriceCode();
}
class RegularPrice extends Price {
int getPriceCode() {
return Movie.REGULAR;
}
}
class NewReleasePrice extends Price {
int getPriceCode() {
return Movie.NEW_RELEASE;
}
}
class ChildrensPrice extends Price {
int getPriceCode() {
return Movie.CHILDRENS;
}
}
36
71
Refactoring
Replace price type codes with instances
of price state classes
72
Refactoring
public class Movie {
private int _priceCode;
public int getPriceCode() {
return _priceCode;
}
public void setPriceCode( int arg ) {
_priceCode = arg;
}
}
37
73
Refactoring
public class Movie {
private Price _price;
public int getPriceCode() {
return _price.getPriceCode();
}
public void setPriceCode( int arg ) {
switch (arg) {
case REGULAR:
_price = new RegularPrice();
break;
case NEW_RELEASE:
_price = new NewReleasePrice();
break;
case CHILDRENS:
_price = new ChildrensPrice();
break;
default:
throw new IllegalArgumentException( Incorrect price code );
}
}
}
74
Refactoring
Move getCharge() to Price class:
Apply Move Method.
38
75
Refactoring
class Movie {
double getCharge( int daysRented ) {
double result = 0;
switch (getPriceCode()) {
case Movie.REGULAR:
result += 2;
if (daysRented > 2)
result += (daysRented - 2) * 1.5;
break;
case Movie.NEW_RELEASE:
result += daysRented * 3;
break;
case Movie.CHILDRENS:
result += 1.5;
if (daysRented > 3)
result += (daysRented - 3) * 1.5;
break;
}
return result;
}
}
76
Refactoring
class Price {
double getCharge( int daysRented ) {
double result = 0;
switch (getPriceCode()) {
case Movie.REGULAR:
result += 2;
if (daysRented > 2)
result += (daysRented - 2) * 1.5;
break;
case Movie.NEW_RELEASE:
result += daysRented * 3;
break;
case Movie.CHILDRENS:
result += 1.5;
if (daysRented > 3)
result += (daysRented - 3) * 1.5;
break;
}
return result;
}
}
39
77
Refactoring
class Movie {
double getCharge( int daysRented ) {
return _price.getCharge( daysRented );
}
}
78
Refactoring
Replace switch statement in
getCharge():
For each case, add overriding method.
Define abstract method.
Apply Replace Conditional with
Polymorphism.
40
79
Refactoring
class Price {
double getCharge( int daysRented ) {
double result = 0;
switch (getPriceCode()) {
case Movie.REGULAR:
result += 2;
if (daysRented > 2)
result += (daysRented - 2) * 1.5;
break;
case Movie.NEW_RELEASE:
result += daysRented * 3;
break;
case Movie.CHILDRENS:
result += 1.5;
if (daysRented > 3)
result += (daysRented - 3) * 1.5;
break;
}
return result;
}
}
80
Refactoring
class RegularPrice {
double getCharge( int daysRented ) {
double result = 2;
if (daysRented > 2)
result += (daysRented - 2) * 1.5;
return result;
}
}
class NewReleasePrice {
double getCharge( int daysRented ) {
return daysRented * 3;
}
}
class ChildrensPrice {
double getCharge( int daysRented ) {
double result = 1.5;
if (daysRented > 3)
result += (daysRented - 3) * 1.5;
}
}
41
81
Refactoring
class Price {
abstract double getCharge( int daysRented );
}
82
Refactoring
Move getFrequentRenterPoints() to
Price class:
Apply Move Method.
42
83
Refactoring
class Movie {
int getFrequentRenterPoints( int daysRented ) {
if ((getPriceCode() == Movie.NEW_RELEASE) &&
daysRented > 1)
return 2;
else
return 1;
}
}
84
Refactoring
class Price {
int getFrequentRenterPoints( int daysRented ) {
if ((getPriceCode() == Movie.NEW_RELEASE) &&
daysRented > 1)
return 2;
else
return 1;
}
}
43
85
Refactoring
class Movie {
int getFrequentRenterPoints( int daysRented ) {
return _price.getFrequentRenterPoints( daysRented );
}
}
86
Refactoring
Replace if statement in
getFrequentRenterPoints():
Apply Replace Conditional with
Polymorphism.
44
87
Refactoring
class Price {
int getFrequentRenterPoints( int daysRented ) {
if ((getPriceCode() == Movie.NEW_RELEASE) &&
daysRented > 1)
return 2;
else
return 1;
}
}
88
Refactoring
class Price {
int getFrequentRenterPoints( int daysRented ) {
return 1;
}
}
class NewReleasePrice {
int getFrequentRenterPoints( int daysRented ) {
return (daysRented > 1) ? 2 : 1;
}
}
45
89
Refactoring
Result of state design pattern:
easy to change price behavior
can add new price codes
rest of application does not know about this
use of the state pattern
90
Refactoring
Benefits of second refactoring:
easy to change movie classifications
easy to change rules for charging and
frequent renter points
46
91
Rental
-daysRented: int
+getCharge(): double
+getFrequentRenterPoints(): int
Customer
+statement()
+getTotalCharge(): double
+getTotalFrequentRenterPoints(): int
+htmlStatement()
Price
Regular Price
+getCharge( days: int ): double
Childrens Price New Release Price
Movie
+getCharge( days: int ): double +getCharge( days: int ): double
+getCharge( days: int ): double
1*
+getCharge( days: int ): double
-price: Price
+getFrequentRenterPoints( days: int ): int +getFrequentRenterPoints( days: int ): int
+getFrequentRenterPoints( days: int ): int
+getDaysRented(): int
*
1
*
92
Refactoring
:Customer :Rental :Movie
getTotalCharge()
getCharge ( days )
statement
getTotalFrequentRenterPoints
* [for all rentals]
getCharge
* [for all rentals]
getFrequentRenterPoints getFrequentRenterPoints ( days )
:Price
getCharge ( days )
getFrequentRenterPoints ( days )
47
93
Bad Smells in Code
Feature envy:
a method seems more interested in a class
other than the one it is actually in
e.g., invoking lots of get methods
can use Move Method and Extract Method
94
Bad Smells in Code
Data clumps:
groups of data appearing together in the fields of
classes, parameters to methods, etc.
e.g., int x, int y, int z
move these groups into their own class
can use Extract Class and Introduce Parameter
Object
Example: group (start: Date, end: Date) into (aRange:
RangeDate)
48
95
Bad Smells in Code
Primitive obsession:
using the built-in types of the language too
much
reluctance to use small objects for small
tasks
e.g., zip code string
use objects for individual data values
can use Replace Data Value with Object
96
Bad Smells in Code
Switch statements:
consider using polymorphism instead
e.g., conditionals on type codes defined in
other classes
can use Extract Method (on the switch),
Move Method, Replace Type Code, and
Replace Conditional with Polymorphism
49
97
Bad Smells in Code
Speculative generality:
I think we might need this someday.
e.g., abstract classes without a real purpose
e.g., unused parameters
can use Collapse Hierarchy and Remove
Parameter
98
Bad Smells in Code
Message chains:
long chains of navigation to get to an
object
e.g., client object talks to server object that
delegates to another object that the client
object must also know about
can use Hide Delegate
50
99
Bad Smells in Code
Middle man:
a class that delegates many methods to
another class
can use Remove Middle Man or Replace
Delegation with Inheritance
but could be a legitimate adapter
100
Bad Smells in Code
Don t stand so close:
two classes that depend too much on each
other, with lots of bidirectional
communication
separate the two classes
can use Move Method, Move Field, and
Extract Class (factor out commonality)
51
101
Bad Smells in Code
Alternative classes with different
interfaces:
methods that do the same thing but have
different signatures
e.g., put() versus add()
can use Rename Method
102
Bad Smells in Code
Data class:
classes that are all data (manipulated by other
classes)
e.g., a Point record that has other classes
manipulating its coordinates
in early stages, it s all right to use public fields
study usage and move behavior into data classes
can use Encapsulate Field, Extract Method, Move
Method
52
103
Bad Smells in Code
Refused bequest:
when a subclass inherits something that is not
needed
when a superclass does not contain truly common
state/behavior
can use Push Down Method and Push Down Field
can use Replace Inheritance with Delegation
(e.g., Square versus Rectangle)
104
Bad Smells in Code
Comments:
often deodorant for bad smelling code
refactor code so that the comment
becomes extraneous
53
105
References
Refactoring
M. Fowler et al.
Addison-Wesley, 1999
UML Distilled
M. Fowler
Addison-Wesley, 2000
This document was created with Win2PDF available at http://www.daneprairie.com.
The unregistered version of Win2PDF is for evaluation or non-commercial use only.

Weitere ähnliche Inhalte

Was ist angesagt?

2014 computer science_question_paper
2014 computer science_question_paper2014 computer science_question_paper
2014 computer science_question_papervandna123
 
An introduction to thrust CUDA
An introduction to thrust CUDAAn introduction to thrust CUDA
An introduction to thrust CUDAAdrien Wattez
 
Oops lab manual2
Oops lab manual2Oops lab manual2
Oops lab manual2Mouna Guru
 
Introduction to Gura Programming Language
Introduction to Gura Programming LanguageIntroduction to Gura Programming Language
Introduction to Gura Programming LanguageYutaka Saito
 
Super TypeScript II Turbo - FP Remix (NG Conf 2017)
Super TypeScript II Turbo - FP Remix (NG Conf 2017)Super TypeScript II Turbo - FP Remix (NG Conf 2017)
Super TypeScript II Turbo - FP Remix (NG Conf 2017)Sean May
 
0853352_Report_Valuing Warrants With VBA
0853352_Report_Valuing Warrants With VBA0853352_Report_Valuing Warrants With VBA
0853352_Report_Valuing Warrants With VBAKartik Malla
 
User defined functions
User defined functionsUser defined functions
User defined functionsshubham_jangid
 
The Ring programming language version 1.4.1 book - Part 17 of 31
The Ring programming language version 1.4.1 book - Part 17 of 31The Ring programming language version 1.4.1 book - Part 17 of 31
The Ring programming language version 1.4.1 book - Part 17 of 31Mahmoud Samir Fayed
 
Practical Class 12th (c++programs+sql queries and output)
Practical Class 12th (c++programs+sql queries and output) Practical Class 12th (c++programs+sql queries and output)
Practical Class 12th (c++programs+sql queries and output) Aman Deep
 
CBSE Grade12, Computer Science, Sample Question Paper
CBSE Grade12, Computer Science, Sample Question PaperCBSE Grade12, Computer Science, Sample Question Paper
CBSE Grade12, Computer Science, Sample Question PaperMalathi Senthil
 

Was ist angesagt? (20)

2014 computer science_question_paper
2014 computer science_question_paper2014 computer science_question_paper
2014 computer science_question_paper
 
An introduction to thrust CUDA
An introduction to thrust CUDAAn introduction to thrust CUDA
An introduction to thrust CUDA
 
Lambda
LambdaLambda
Lambda
 
Oops lab manual2
Oops lab manual2Oops lab manual2
Oops lab manual2
 
Introduction to Gura Programming Language
Introduction to Gura Programming LanguageIntroduction to Gura Programming Language
Introduction to Gura Programming Language
 
Super TypeScript II Turbo - FP Remix (NG Conf 2017)
Super TypeScript II Turbo - FP Remix (NG Conf 2017)Super TypeScript II Turbo - FP Remix (NG Conf 2017)
Super TypeScript II Turbo - FP Remix (NG Conf 2017)
 
0853352_Report_Valuing Warrants With VBA
0853352_Report_Valuing Warrants With VBA0853352_Report_Valuing Warrants With VBA
0853352_Report_Valuing Warrants With VBA
 
Advanced JavaScript
Advanced JavaScript Advanced JavaScript
Advanced JavaScript
 
User defined functions
User defined functionsUser defined functions
User defined functions
 
Lecture 12: Classes and Files
Lecture 12: Classes and FilesLecture 12: Classes and Files
Lecture 12: Classes and Files
 
C++ manual Report Full
C++ manual Report FullC++ manual Report Full
C++ manual Report Full
 
Computer Programming- Lecture 4
Computer Programming- Lecture 4Computer Programming- Lecture 4
Computer Programming- Lecture 4
 
The Ring programming language version 1.4.1 book - Part 17 of 31
The Ring programming language version 1.4.1 book - Part 17 of 31The Ring programming language version 1.4.1 book - Part 17 of 31
The Ring programming language version 1.4.1 book - Part 17 of 31
 
Bijender (1)
Bijender (1)Bijender (1)
Bijender (1)
 
4. functions
4. functions4. functions
4. functions
 
Functions123
Functions123 Functions123
Functions123
 
Functions12
Functions12Functions12
Functions12
 
Practical Class 12th (c++programs+sql queries and output)
Practical Class 12th (c++programs+sql queries and output) Practical Class 12th (c++programs+sql queries and output)
Practical Class 12th (c++programs+sql queries and output)
 
CBSE Grade12, Computer Science, Sample Question Paper
CBSE Grade12, Computer Science, Sample Question PaperCBSE Grade12, Computer Science, Sample Question Paper
CBSE Grade12, Computer Science, Sample Question Paper
 
Struct examples
Struct examplesStruct examples
Struct examples
 

Ähnlich wie Refactoring Example

Refactoring Simple Example
Refactoring Simple ExampleRefactoring Simple Example
Refactoring Simple Exampleliufabin 66688
 
Refactoring in AS3
Refactoring in AS3Refactoring in AS3
Refactoring in AS3Eddie Kao
 
Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...Mario Fusco
 
Simple Commsion Calculationbuild.xmlBuilds, tests, and runs t.docx
Simple Commsion Calculationbuild.xmlBuilds, tests, and runs t.docxSimple Commsion Calculationbuild.xmlBuilds, tests, and runs t.docx
Simple Commsion Calculationbuild.xmlBuilds, tests, and runs t.docxbudabrooks46239
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modelingMario Fusco
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modelingCodemotion
 
Pragmatic functional refactoring with java 8 (1)
Pragmatic functional refactoring with java 8 (1)Pragmatic functional refactoring with java 8 (1)
Pragmatic functional refactoring with java 8 (1)RichardWarburton
 
C# Programming. Using methods to call results to display. The code i.pdf
C# Programming. Using methods to call results to display. The code i.pdfC# Programming. Using methods to call results to display. The code i.pdf
C# Programming. Using methods to call results to display. The code i.pdffatoryoutlets
 
Performance measurement and tuning
Performance measurement and tuningPerformance measurement and tuning
Performance measurement and tuningAOE
 
Nagios Conference 2012 - Dave Josephsen - Stop Being Lazy
Nagios Conference 2012 - Dave Josephsen - Stop Being LazyNagios Conference 2012 - Dave Josephsen - Stop Being Lazy
Nagios Conference 2012 - Dave Josephsen - Stop Being LazyNagios
 
Pro typescript.ch03.Object Orientation in TypeScript
Pro typescript.ch03.Object Orientation in TypeScriptPro typescript.ch03.Object Orientation in TypeScript
Pro typescript.ch03.Object Orientation in TypeScriptSeok-joon Yun
 
Applying Linear Optimization Using GLPK
Applying Linear Optimization Using GLPKApplying Linear Optimization Using GLPK
Applying Linear Optimization Using GLPKJeremy Chen
 
Standardizing JavaScript Decorators in TC39 (Full Stack Fest 2019)
Standardizing JavaScript Decorators in TC39 (Full Stack Fest 2019)Standardizing JavaScript Decorators in TC39 (Full Stack Fest 2019)
Standardizing JavaScript Decorators in TC39 (Full Stack Fest 2019)Igalia
 
Python Yield
Python YieldPython Yield
Python Yieldyangjuven
 

Ähnlich wie Refactoring Example (20)

Android Refactoring
Android RefactoringAndroid Refactoring
Android Refactoring
 
Refactoring Simple Example
Refactoring Simple ExampleRefactoring Simple Example
Refactoring Simple Example
 
Refactoring in AS3
Refactoring in AS3Refactoring in AS3
Refactoring in AS3
 
Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...
 
Simple Commsion Calculationbuild.xmlBuilds, tests, and runs t.docx
Simple Commsion Calculationbuild.xmlBuilds, tests, and runs t.docxSimple Commsion Calculationbuild.xmlBuilds, tests, and runs t.docx
Simple Commsion Calculationbuild.xmlBuilds, tests, and runs t.docx
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
 
Pragmatic functional refactoring with java 8 (1)
Pragmatic functional refactoring with java 8 (1)Pragmatic functional refactoring with java 8 (1)
Pragmatic functional refactoring with java 8 (1)
 
Refactoring
RefactoringRefactoring
Refactoring
 
C# Programming. Using methods to call results to display. The code i.pdf
C# Programming. Using methods to call results to display. The code i.pdfC# Programming. Using methods to call results to display. The code i.pdf
C# Programming. Using methods to call results to display. The code i.pdf
 
Performance measurement and tuning
Performance measurement and tuningPerformance measurement and tuning
Performance measurement and tuning
 
Nagios Conference 2012 - Dave Josephsen - Stop Being Lazy
Nagios Conference 2012 - Dave Josephsen - Stop Being LazyNagios Conference 2012 - Dave Josephsen - Stop Being Lazy
Nagios Conference 2012 - Dave Josephsen - Stop Being Lazy
 
Final DAA_prints.pdf
Final DAA_prints.pdfFinal DAA_prints.pdf
Final DAA_prints.pdf
 
7720
77207720
7720
 
Android workshop
Android workshopAndroid workshop
Android workshop
 
Topic 1.3
Topic 1.3Topic 1.3
Topic 1.3
 
Pro typescript.ch03.Object Orientation in TypeScript
Pro typescript.ch03.Object Orientation in TypeScriptPro typescript.ch03.Object Orientation in TypeScript
Pro typescript.ch03.Object Orientation in TypeScript
 
Applying Linear Optimization Using GLPK
Applying Linear Optimization Using GLPKApplying Linear Optimization Using GLPK
Applying Linear Optimization Using GLPK
 
Standardizing JavaScript Decorators in TC39 (Full Stack Fest 2019)
Standardizing JavaScript Decorators in TC39 (Full Stack Fest 2019)Standardizing JavaScript Decorators in TC39 (Full Stack Fest 2019)
Standardizing JavaScript Decorators in TC39 (Full Stack Fest 2019)
 
Python Yield
Python YieldPython Yield
Python Yield
 

Mehr von liufabin 66688

人大2010新生住宿指南
人大2010新生住宿指南人大2010新生住宿指南
人大2010新生住宿指南liufabin 66688
 
中山大学2010新生住宿指南
中山大学2010新生住宿指南中山大学2010新生住宿指南
中山大学2010新生住宿指南liufabin 66688
 
武汉大学2010新生住宿指南
武汉大学2010新生住宿指南武汉大学2010新生住宿指南
武汉大学2010新生住宿指南liufabin 66688
 
天津大学2010新生住宿指南
天津大学2010新生住宿指南天津大学2010新生住宿指南
天津大学2010新生住宿指南liufabin 66688
 
清华2010新生住宿指南
清华2010新生住宿指南清华2010新生住宿指南
清华2010新生住宿指南liufabin 66688
 
南京大学2010新生住宿指南
南京大学2010新生住宿指南南京大学2010新生住宿指南
南京大学2010新生住宿指南liufabin 66688
 
复旦2010新生住宿指南
复旦2010新生住宿指南复旦2010新生住宿指南
复旦2010新生住宿指南liufabin 66688
 
北京师范大学2010新生住宿指南
北京师范大学2010新生住宿指南北京师范大学2010新生住宿指南
北京师范大学2010新生住宿指南liufabin 66688
 
北大2010新生住宿指南
北大2010新生住宿指南北大2010新生住宿指南
北大2010新生住宿指南liufabin 66688
 
Mysql Replication Excerpt 5.1 En
Mysql Replication Excerpt 5.1 EnMysql Replication Excerpt 5.1 En
Mysql Replication Excerpt 5.1 Enliufabin 66688
 
Drupal Con My Sql Ha 2008 08 29
Drupal Con My Sql Ha 2008 08 29Drupal Con My Sql Ha 2008 08 29
Drupal Con My Sql Ha 2008 08 29liufabin 66688
 
Application Partitioning Wp
Application Partitioning WpApplication Partitioning Wp
Application Partitioning Wpliufabin 66688
 
090507.New Replication Features(2)
090507.New Replication Features(2)090507.New Replication Features(2)
090507.New Replication Features(2)liufabin 66688
 
High Performance Mysql
High Performance MysqlHigh Performance Mysql
High Performance Mysqlliufabin 66688
 
high performance mysql
high performance mysqlhigh performance mysql
high performance mysqlliufabin 66688
 
Refactoring And Unit Testing
Refactoring And Unit TestingRefactoring And Unit Testing
Refactoring And Unit Testingliufabin 66688
 

Mehr von liufabin 66688 (20)

人大2010新生住宿指南
人大2010新生住宿指南人大2010新生住宿指南
人大2010新生住宿指南
 
中山大学2010新生住宿指南
中山大学2010新生住宿指南中山大学2010新生住宿指南
中山大学2010新生住宿指南
 
武汉大学2010新生住宿指南
武汉大学2010新生住宿指南武汉大学2010新生住宿指南
武汉大学2010新生住宿指南
 
天津大学2010新生住宿指南
天津大学2010新生住宿指南天津大学2010新生住宿指南
天津大学2010新生住宿指南
 
清华2010新生住宿指南
清华2010新生住宿指南清华2010新生住宿指南
清华2010新生住宿指南
 
南京大学2010新生住宿指南
南京大学2010新生住宿指南南京大学2010新生住宿指南
南京大学2010新生住宿指南
 
复旦2010新生住宿指南
复旦2010新生住宿指南复旦2010新生住宿指南
复旦2010新生住宿指南
 
北京师范大学2010新生住宿指南
北京师范大学2010新生住宿指南北京师范大学2010新生住宿指南
北京师范大学2010新生住宿指南
 
北大2010新生住宿指南
北大2010新生住宿指南北大2010新生住宿指南
北大2010新生住宿指南
 
Optimzing mysql
Optimzing mysqlOptimzing mysql
Optimzing mysql
 
Mysql Replication Excerpt 5.1 En
Mysql Replication Excerpt 5.1 EnMysql Replication Excerpt 5.1 En
Mysql Replication Excerpt 5.1 En
 
Drupal Con My Sql Ha 2008 08 29
Drupal Con My Sql Ha 2008 08 29Drupal Con My Sql Ha 2008 08 29
Drupal Con My Sql Ha 2008 08 29
 
Application Partitioning Wp
Application Partitioning WpApplication Partitioning Wp
Application Partitioning Wp
 
090507.New Replication Features(2)
090507.New Replication Features(2)090507.New Replication Features(2)
090507.New Replication Features(2)
 
Mysql Replication
Mysql ReplicationMysql Replication
Mysql Replication
 
High Performance Mysql
High Performance MysqlHigh Performance Mysql
High Performance Mysql
 
high performance mysql
high performance mysqlhigh performance mysql
high performance mysql
 
Lecture28
Lecture28Lecture28
Lecture28
 
Refactoring And Unit Testing
Refactoring And Unit TestingRefactoring And Unit Testing
Refactoring And Unit Testing
 
Refactoring 重构
Refactoring 重构Refactoring 重构
Refactoring 重构
 

Refactoring Example

  • 1. 1 CMPUT 301: Lecture 14 Refactoring Lecturer: Sherif Ghali Department of Computing Science University of Alberta Notes credits: Ken Wong, Sherif Ghali (Some slides removed by M. Hicks) 2 Refactoring Example: a program to calculate and print a statement of a customer s charges at a video store
  • 2. 2 3 Class Diagram -priceCode : int Movie Rental -daysRented : int Customer +statement() * 1 * +getDaysRented (): int +getPriceCode (): int 4 Movie 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; 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; } }
  • 3. 3 5 Rental class 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; } } 6 Customer class 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; } }
  • 4. 4 7 Customer::statement() 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(); // 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.CHILDRENS: thisAmount += 1.5; if (each.getDaysRented() > 3) thisAmount += (each.getDaysRented() - 3) * 1.5; break; } 8 Customer::statement() // add frequent renter points 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; } }
  • 5. 5 9 Refactoring :Customer :Rental :Movie * [for all rentals] getMovie () getPriceCode () getDaysRented () statement() 10 Refactoring Something is rotten in the state of Denmark What is it?
  • 6. 6 11 Refactoring Issues: not object-oriented statement( ) routine does too much Customer class is a blob potentially difficult to make changes e.g., HTML output e.g., new charging rules 12 Refactoring Idea: If the code is not structured conveniently to add a feature, first refactor the program to make it easy to add the feature, then add the feature.
  • 7. 7 13 Refactoring First step: Build self-checking tests. 14 Refactoring Decompose statement() method: Extract logical chunk of code as a new method. Apply Extract Method.
  • 8. 8 15 class Customer { 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(); // 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.CHILDRENS: thisAmount += 1.5; if (each.getDaysRented() > 3) thisAmount += (each.getDaysRented() - 3) * 1.5; break; } 16 Refactoring class Customer { 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 );
  • 9. 9 17 Refactoring class Customer { private double amountFor( Rental each ) { double thisAmount = 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.CHILDRENS: thisAmount += 1.5; if (each.getDaysRented() > 3) thisAmount += (each.getDaysRented() - 3) * 1.5; break; } return thisAmount; } } 18 Refactoring Compile and test! small steps What do we do next?
  • 10. 10 19 Refactoring Rename variables in amountFor(): Enhance readability. 20 Refactoring class Customer { private double amountFor( Rental each ) { double thisAmount = 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.CHILDRENS: thisAmount += 1.5; if (each.getDaysRented() > 3) thisAmount += (each.getDaysRented() - 3) * 1.5; break; } return thisAmount; } }
  • 11. 11 21 Refactoring class Customer { private double amountFor( Rental aRental ) { double result = 0; switch (aRental.getMovie().getPriceCode()) { case Movie.REGULAR: result += 2; if (aRental.getDaysRented() > 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() > 3) result += (aRental.getDaysRented() - 3) * 1.5; break; } return result; } } 22 Refactoring Compile and test. What do we do next?
  • 12. 12 23 Refactoring Move amountFor() to Rental class: Method uses rental information, but not customer information. Move method to the right class. Apply Move Method. 24 Refactoring class Customer { private double amountFor( Rental aRental ) { double result = 0; switch (aRental.getMovie().getPriceCode()) { case Movie.REGULAR: result += 2; if (aRental.getDaysRented() > 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() > 3) result += (aRental.getDaysRented() - 3) * 1.5; break; } return result; } }
  • 13. 13 25 Refactoring class Rental { double getCharge() { double result = 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.CHILDRENS: result += 1.5; if (getDaysRented() > 3) result += (getDaysRented() - 3) * 1.5; break; } return result; } } 26 Refactoring class Customer { private double amountFor( Rental aRental ) { return aRental.getCharge(); } }
  • 14. 14 27 Refactoring Compile and test. 28 Refactoring Replace references to amountFor() with getCharge(): Adjust references to old method to use new method. Remove old method.
  • 15. 15 29 Refactoring class Customer { 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 ); 30 Refactoring class Customer { 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();
  • 16. 16 31 Refactoring Compile and test. 32 Refactoring Eliminate thisAmount temporary in statement(): Replace redundant temporary variable with query. Apply Replace Temp with Query.
  • 17. 17 33 Refactoring class Customer { 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(); // add frequent renter points 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; } 34 Refactoring class Customer { 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(); // add frequent renter points 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( each.getCharge() ) + n ; totalAmount += each.getCharge(); }
  • 18. 18 35 Refactoring Extract frequent renter points logic: Applicable rules belong to the rental, not the customer. Apply Extract Method and Move Method. 36 Refactoring class Customer { 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(); // add frequent renter points 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( each.getCharge() ) + n ; totalAmount += each.getCharge(); }
  • 19. 19 37 Refactoring class Customer { 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(); // add frequent renter points frequentRenterPoints += each.getFrequentRenterPoints(); // show figures for this rental result += t + each.getMovie().getTitle() + t + String.valueOf( each.getCharge() ) + n ; totalAmount += each.getCharge(); } 38 Refactoring class Rental { int getFrequentRenterPoints() { if ((getMovie().getPriceCode() == Movie.NEW_RELEASE) && getDaysRented() > 1) return 2; else return 1; } }
  • 20. 20 39 Refactoring Eliminate totalAmount temporary: Apply Replace Temp with Query. 40 Refactoring class Customer { 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(); // add frequent renter points frequentRenterPoints += each.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 ) + n ; result += You earned + String.valueOf( frequentRenterPoints ) + frequent renter points ; return result; } }
  • 21. 21 41 class Customer { 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(); // add frequent renter points frequentRenterPoints += each.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 ) + frequent renter points ; return result; } } Refactoring 42 class Customer { private double getTotalCharge() { double result = 0; Enumeration rentals = _rentals.elements(); while (rentals.hasMoreElements()) { Rental each = (Rental)rentals.nextElement(); result += each.getCharge(); } return result; } } Refactoring
  • 22. 22 43 Refactoring Eliminate frequentRenterPoints temporary: Apply Replace Temp with Query. 44 class Customer { 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(); // add frequent renter points frequentRenterPoints += each.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 ) + frequent renter points ; return result; } } Refactoring
  • 23. 23 45 Refactoring class Customer { public String statement() { Enumeration rentals = _rentals.elements(); String result = Rental Record for + getName() + n ; while (rentals.hasMoreElements()) { Rental each = (Rental)rentals.nextElement(); // 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( getTotalFrequentRenterPoints() ) + frequent renter points ; return result; } } 46 Refactoring class Customer { private int getTotalFrequentRenterPoints() { int result = 0; Enumeration rentals = _rentals.elements(); while (rentals.hasMoreElements()) { Rental each = (Rental)rentals.nextElement(); result += each.getFrequentRenterPoints(); } return result; } }
  • 24. 24 47 Refactoring -priceCode : int Movie Rental -daysRented : int +getCharge (): double +getFrequentRenterPoints (): int Customer +statement() -getTotalCharge (): double -getTotalFrequentRenterPoints (): int * 1 * +getPriceCode (): int +getDaysRented (): int 48 Refactoring :Customer :Rental :Movie getTotalCharge () getPriceCode () statement() getTotalFrequentRenterPoints () * [for all rentals] getCharge () * [for all rentals] getFrequentRenterPoints () getPriceCode ()
  • 26. 26 51 Refactoring New feature (HTML output): class Customer { public String htmlStatement() { Enumeration rentals = _rentals.elements(); String result = <H1>Rental Record for + getName() + </H1>n ; while (rentals.hasMoreElements()) { Rental each = (Rental)rentals.nextElement(); // show figures for this rental result += each.getMovie().getTitle() + : + String.valueOf( each.getCharge() ) + <BR>n ; } // add footer lines result += <P>Amount owed is + String.valueOf( getTotalCharge() ) + </P>n ; result += <P>You earned + String.valueOf( getTotalFrequentRenterPoints() ) + frequent renter points</P> ; return result; } } 52 Refactoring More needs: new classifications of movies
  • 27. 27 53 Refactoring class Rental { double getCharge() { double result = 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.CHILDRENS: result += 1.5; if (getDaysRented() > 3) result += (getDaysRented() - 3) * 1.5; break; } return result; } } 54 Refactoring Replace conditional logic on price code with polymorphism: Rental logic should not depend on specific movie types. It is generally bad design to do a switch on an another object s attribute.
  • 28. 28 55 Refactoring class Movie { double getCharge( int daysRented ) { double result = 0; switch (getPriceCode()) { case Movie.REGULAR: result += 2; if (daysRented > 2) result += (daysRented - 2) * 1.5; break; case Movie.NEW_RELEASE: result += daysRented * 3; break; case Movie.CHILDRENS: result += 1.5; if (daysRented > 3) result += (daysRented - 3) * 1.5; break; } return result; } } 56 Refactoring class Rental { double getCharge() { return _movie.getCharge( _daysRented ); } }
  • 29. 29 57 Refactoring class Rental { int getFrequentRenterPoints() { if ((getMovie().getPriceCode() == Movie.NEW_RELEASE) && getDaysRented() > 1) return 2; else return 1; } } 58 Refactoring class Movie { int getFrequentRenterPoints( int daysRented ) { if ((getPriceCode() == Movie.NEW_RELEASE) && daysRented > 1) return 2; else return 1; } }
  • 30. 30 59 Refactoring class Rental { int getFrequentRenterPoints() { return _movie.getFrequentRenterPoints( _daysRented ); } } 60 Refactoring Get rid of the switch statement: class Movie { double getCharge( int daysRented ) { double result = 0; switch (getPriceCode()) { case Movie.REGULAR: result += 2; if (daysRented > 2) result += (daysRented - 2) * 1.5; break; case Movie.NEW_RELEASE: result += daysRented * 3; break; case Movie.CHILDRENS: result += 1.5; if (daysRented > 3) result += (daysRented - 3) * 1.5; break; } return result; } }
  • 31. 31 61 Refactoring ? -priceCode : int Movie +getCharge ( days: int ): double +getCharge ( days: int ): double +getCharge ( days: int ): double+getCharge ( days: int ): double Regular Movie Childrens Movie New Release Movie 62 Refactoring We have two flaws. What are they? Hint: Is the movie classification static?
  • 32. 32 63 Refactoring Flaw: A movie may change its classification during its lifetime. An object cannot change its class during its lifetime. 64 Refactoring Solution: Use State design pattern.
  • 33. 33 65 Refactoring Price Regular Price +getCharge( days: int ): double Childrens Price New Release Price Movie return price.getCharge ( days ) +getCharge( days: int ): double +getCharge( days: int ): double +getCharge( days: int ): double 1* +getCharge( days: int ): double 66 Refactoring Replace price (type) code: Apply Replace Type Code with State. Compile and test after each step.
  • 34. 34 67 Refactoring Note: Make sure uses of the price type code go through accessor methods 68 Refactoring public class Movie { 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; } }
  • 35. 35 69 Refactoring public class Movie { private int _priceCode; public Movie( String title, int priceCode ) { _title = title; setPriceCode( priceCode ); } public int getPriceCode() { return _priceCode; } public void setPriceCode( int arg ) { _priceCode = arg; } } 70 Refactoring Add new state classes: abstract class Price { abstract int getPriceCode(); } class RegularPrice extends Price { int getPriceCode() { return Movie.REGULAR; } } class NewReleasePrice extends Price { int getPriceCode() { return Movie.NEW_RELEASE; } } class ChildrensPrice extends Price { int getPriceCode() { return Movie.CHILDRENS; } }
  • 36. 36 71 Refactoring Replace price type codes with instances of price state classes 72 Refactoring public class Movie { private int _priceCode; public int getPriceCode() { return _priceCode; } public void setPriceCode( int arg ) { _priceCode = arg; } }
  • 37. 37 73 Refactoring public class Movie { private Price _price; public int getPriceCode() { return _price.getPriceCode(); } public void setPriceCode( int arg ) { switch (arg) { case REGULAR: _price = new RegularPrice(); break; case NEW_RELEASE: _price = new NewReleasePrice(); break; case CHILDRENS: _price = new ChildrensPrice(); break; default: throw new IllegalArgumentException( Incorrect price code ); } } } 74 Refactoring Move getCharge() to Price class: Apply Move Method.
  • 38. 38 75 Refactoring class Movie { double getCharge( int daysRented ) { double result = 0; switch (getPriceCode()) { case Movie.REGULAR: result += 2; if (daysRented > 2) result += (daysRented - 2) * 1.5; break; case Movie.NEW_RELEASE: result += daysRented * 3; break; case Movie.CHILDRENS: result += 1.5; if (daysRented > 3) result += (daysRented - 3) * 1.5; break; } return result; } } 76 Refactoring class Price { double getCharge( int daysRented ) { double result = 0; switch (getPriceCode()) { case Movie.REGULAR: result += 2; if (daysRented > 2) result += (daysRented - 2) * 1.5; break; case Movie.NEW_RELEASE: result += daysRented * 3; break; case Movie.CHILDRENS: result += 1.5; if (daysRented > 3) result += (daysRented - 3) * 1.5; break; } return result; } }
  • 39. 39 77 Refactoring class Movie { double getCharge( int daysRented ) { return _price.getCharge( daysRented ); } } 78 Refactoring Replace switch statement in getCharge(): For each case, add overriding method. Define abstract method. Apply Replace Conditional with Polymorphism.
  • 40. 40 79 Refactoring class Price { double getCharge( int daysRented ) { double result = 0; switch (getPriceCode()) { case Movie.REGULAR: result += 2; if (daysRented > 2) result += (daysRented - 2) * 1.5; break; case Movie.NEW_RELEASE: result += daysRented * 3; break; case Movie.CHILDRENS: result += 1.5; if (daysRented > 3) result += (daysRented - 3) * 1.5; break; } return result; } } 80 Refactoring class RegularPrice { double getCharge( int daysRented ) { double result = 2; if (daysRented > 2) result += (daysRented - 2) * 1.5; return result; } } class NewReleasePrice { double getCharge( int daysRented ) { return daysRented * 3; } } class ChildrensPrice { double getCharge( int daysRented ) { double result = 1.5; if (daysRented > 3) result += (daysRented - 3) * 1.5; } }
  • 41. 41 81 Refactoring class Price { abstract double getCharge( int daysRented ); } 82 Refactoring Move getFrequentRenterPoints() to Price class: Apply Move Method.
  • 42. 42 83 Refactoring class Movie { int getFrequentRenterPoints( int daysRented ) { if ((getPriceCode() == Movie.NEW_RELEASE) && daysRented > 1) return 2; else return 1; } } 84 Refactoring class Price { int getFrequentRenterPoints( int daysRented ) { if ((getPriceCode() == Movie.NEW_RELEASE) && daysRented > 1) return 2; else return 1; } }
  • 43. 43 85 Refactoring class Movie { int getFrequentRenterPoints( int daysRented ) { return _price.getFrequentRenterPoints( daysRented ); } } 86 Refactoring Replace if statement in getFrequentRenterPoints(): Apply Replace Conditional with Polymorphism.
  • 44. 44 87 Refactoring class Price { int getFrequentRenterPoints( int daysRented ) { if ((getPriceCode() == Movie.NEW_RELEASE) && daysRented > 1) return 2; else return 1; } } 88 Refactoring class Price { int getFrequentRenterPoints( int daysRented ) { return 1; } } class NewReleasePrice { int getFrequentRenterPoints( int daysRented ) { return (daysRented > 1) ? 2 : 1; } }
  • 45. 45 89 Refactoring Result of state design pattern: easy to change price behavior can add new price codes rest of application does not know about this use of the state pattern 90 Refactoring Benefits of second refactoring: easy to change movie classifications easy to change rules for charging and frequent renter points
  • 46. 46 91 Rental -daysRented: int +getCharge(): double +getFrequentRenterPoints(): int Customer +statement() +getTotalCharge(): double +getTotalFrequentRenterPoints(): int +htmlStatement() Price Regular Price +getCharge( days: int ): double Childrens Price New Release Price Movie +getCharge( days: int ): double +getCharge( days: int ): double +getCharge( days: int ): double 1* +getCharge( days: int ): double -price: Price +getFrequentRenterPoints( days: int ): int +getFrequentRenterPoints( days: int ): int +getFrequentRenterPoints( days: int ): int +getDaysRented(): int * 1 * 92 Refactoring :Customer :Rental :Movie getTotalCharge() getCharge ( days ) statement getTotalFrequentRenterPoints * [for all rentals] getCharge * [for all rentals] getFrequentRenterPoints getFrequentRenterPoints ( days ) :Price getCharge ( days ) getFrequentRenterPoints ( days )
  • 47. 47 93 Bad Smells in Code Feature envy: a method seems more interested in a class other than the one it is actually in e.g., invoking lots of get methods can use Move Method and Extract Method 94 Bad Smells in Code Data clumps: groups of data appearing together in the fields of classes, parameters to methods, etc. e.g., int x, int y, int z move these groups into their own class can use Extract Class and Introduce Parameter Object Example: group (start: Date, end: Date) into (aRange: RangeDate)
  • 48. 48 95 Bad Smells in Code Primitive obsession: using the built-in types of the language too much reluctance to use small objects for small tasks e.g., zip code string use objects for individual data values can use Replace Data Value with Object 96 Bad Smells in Code Switch statements: consider using polymorphism instead e.g., conditionals on type codes defined in other classes can use Extract Method (on the switch), Move Method, Replace Type Code, and Replace Conditional with Polymorphism
  • 49. 49 97 Bad Smells in Code Speculative generality: I think we might need this someday. e.g., abstract classes without a real purpose e.g., unused parameters can use Collapse Hierarchy and Remove Parameter 98 Bad Smells in Code Message chains: long chains of navigation to get to an object e.g., client object talks to server object that delegates to another object that the client object must also know about can use Hide Delegate
  • 50. 50 99 Bad Smells in Code Middle man: a class that delegates many methods to another class can use Remove Middle Man or Replace Delegation with Inheritance but could be a legitimate adapter 100 Bad Smells in Code Don t stand so close: two classes that depend too much on each other, with lots of bidirectional communication separate the two classes can use Move Method, Move Field, and Extract Class (factor out commonality)
  • 51. 51 101 Bad Smells in Code Alternative classes with different interfaces: methods that do the same thing but have different signatures e.g., put() versus add() can use Rename Method 102 Bad Smells in Code Data class: classes that are all data (manipulated by other classes) e.g., a Point record that has other classes manipulating its coordinates in early stages, it s all right to use public fields study usage and move behavior into data classes can use Encapsulate Field, Extract Method, Move Method
  • 52. 52 103 Bad Smells in Code Refused bequest: when a subclass inherits something that is not needed when a superclass does not contain truly common state/behavior can use Push Down Method and Push Down Field can use Replace Inheritance with Delegation (e.g., Square versus Rectangle) 104 Bad Smells in Code Comments: often deodorant for bad smelling code refactor code so that the comment becomes extraneous
  • 53. 53 105 References Refactoring M. Fowler et al. Addison-Wesley, 1999 UML Distilled M. Fowler Addison-Wesley, 2000
  • 54. This document was created with Win2PDF available at http://www.daneprairie.com. The unregistered version of Win2PDF is for evaluation or non-commercial use only.