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 Class public class Movie Movie { 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 } public int getPriceCode() { return _priceCode; }
20
Embed
Refactoring Refactoring, applied - Purdue Universityprogram’s code is not structured in a convenient way to add it, refactor the code. C Refactoring: step 1 Write a test suite !
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
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 {
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
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)
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
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
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
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
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
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
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()+ “: ” +
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