Title: Refactoring
1Refactoring
SQI, Griffith University
2What is Refactoring
- When you recognize that the design, tests, or
code proper have structural flaws, you need to
refactor. - Refactoring is the process of changing a software
system such that the external behavior of the
system does not change - functional requirements are maintained but the
internal structure of the system is improved - that is, you do not modify the feature set at all
3Why?
- To improve the quality of the codebase
- Makes software easier to understand, therefore
maintain, extend, verify,... - It is difficult to get a design right the first
time (iterative and incremental) - Systems evolve maintenance is highest cost in
SDLC.
4What refactoring isnt
- Refactoring is not an opportunity to add features
- Refactoring is not performance optimisation which
often leads to code that is harder to understand.
5When?
- Rule of three
- If you do the same thing the same three times in
an implementation its time to refactor to remove
duplication. - Refactor when you add new functionality
- before to make it easier to incorporate new
function - after to clean up
- 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 - Refactor when you fix a defect
- Refactor as part of code review process (edit)
6When not to?
- When it is easier to start again
- Refactoring often requires changes to the
interface, so refactoring is not a good idea
when - you do not own all of the calling code
- you have published the interface for other
clients use - Many refactorings have an adverse effect on
efficiency (time/resource) if efficiency is a
higher architectural priority than
maintainability, dont apply refactorings if they
will effect efficiency. - This is rarely the case these days (exceptions
include things like embedded and/or real-time
systems).
7Safe Refactoring
- Use refactoring patterns
- Fowler et. al., Refactoring Improving the
Design of Existing Code - Test constantly
- Ensure you have an adequate set of tests before
refactoring - Make sure you are 100 compliant with tests
before refactoring - Refactor
- Make sure you are 100 compliant with tests after
refactoring - If program fails a test, you know about it
straight away
8An example Refactoring
- Encapsulate Field
- To make clients of an object use methods to
access a field rather than the field directly - Encapsulate Field (cookbook)
- Create accessor and mutator methods for the field
- Locate all references to the field and replace
all calls to field with calls to accessor and all
changes to the field with calls to the mutator. - Compile and test after changing each reference
- Declare the field as private
- Compile and test
- Refactoring works in tiny steps
9An example Refactoring
- Original Code
- public class Person public String name
- Original Client
- ...
- Person person
- person.name Joe Bloggs
- assertEquals(Joe Bloggs, person.name)
- ...
10An example Refactoring
- Step 1 Create Accessors and Mutators
- public class Person
- public String name
- public String getName() return name
- public void setName(String n) name n
-
- Client
- ...
- Person person
- person.name Joe Bloggs
- assertEquals(Joe Bloggs, person.name)
- ...
11An example Refactoring
- Step 2 Replace direct references with accessors
and mutators - public class Person
- public String name
- public String getName() return name
- public void setName(String n) name n
-
- New Client
- ...
- Person person
- person.setName(Joe Bloggs)
- assertEquals(Joe Bloggs, person.name)
- ...
Compile and Test
12An example Refactoring
- Step 2 Replace direct references with accessors
and mutators - public class Person
- public String name
- public String getName() return name
- public void setName(String n) name n
-
- New Client
- ...
- Person person
- person.setName(Joe Bloggs)
- assertEquals(Joe Bloggs, person.getName())
- ...
Compile and Test
13An example Refactoring
- Step 3 Make Field private
- public class Person
- private String name
- public String getName() return name
- public void setName(String n) name n
-
- New Client
- ...
- Person person
- person.setName(Joe Bloggs)
- assertEquals(Joe Bloggs, person.getName())
- ...
Compile and Test
14How? Look for Bad Smells - Fowler
- Duplicated Code
- Long methods
- Large Classes
- Long parameter lists
- Divergent Change
- Shotgun Surgery
- Feature Envy
- Data Clumps
- Primitive Obsession
- Switch statements
- Parallel Inheritance Hierarchies
- Lazy Class
- Speculative Generality
- Temporary Field
- Message Chain
- Middle Man
- Inappropriate Intimacy
- Alternate classes different names
- Incomplete Library Class
- Data Class
- Refused Bequest
- Comments
15Some Common Refactorings
- Extract Method
- Replace Temp with Query
- Move Method
16Extract Method
- When?
- Duplicate Code
- extract out common code into separate method
- Long Method
- extract a number of smaller, more cohesive
methods - Comments
- extract individually commented blocks to a
separate method
17Extract Method - Example
- public double correlation(double x, double
y, int len) - // calculate sum of xy products
- int i 0 double sumProd 0
- while (i lt len)
- sumProd sumProd xiyi
-
- ...
- // calculate sum of squares x
- i 0 double sumSquaresX 0
- while (i lt len)
- sumSquaresX sumSquaresX xixi
-
- // calculate sum of squares y
- i 0 double sumSquaresY 0
- while (i lt len)
- sumSquaresY sumSquaresY yiyi
-
- ...
18Extract Methods
- Smells, Symtoms and Refactorings
- Comments in code commenting blocks
- extract method extract separate methods for
each block - Duplicate Code
- extract method extract common code to a single
method
19Original - Comments
- // calculate sum of xy products
- int i 0 double sumProd 0
- while (i lt len)
- sumProd sumProd xiyi
-
-
20Refactoring - Comments
- public double sumProd( double x,double y,
int len ) - int i 0 double sumProd 0
- while (i lt len)
- sumProd sumProdxiyi
-
- return sumProd
-
-
- double sumProd sumProd(x,y,len)
21Original Duplicate Code
- // calculate sum of squares x
- i 0 double sumSquaresX 0
- while (i lt len)
- sumSquaresXsumSquaresXxixi
-
- // calculate sum of squares y
- i 0 double sumSquaresY 0
- while (i lt len)
- sumSquaresYsumSquaresYyiyi
-
22Refactoring Duplicate Code
- public double sumSquares(double a, int len )
- int i 0 double sumSquares 0
- while (i lt len)
- sumSquaressumSquaresaiai
-
- return sumSquares
-
-
- double sumSquaresXsumSquares(x,len)
- double sumSquaresYsumSquares(y,len)
23Refactoring
- public double correlation(double x, double
y, int len) - ...
- double sumProd sumProd(x,y,len)
- double sumSquaresX sumSquares(x,len)
- double sumSquaresY sumSquares(y,len)
- ...
24Long Method
- Symptoms
- Large number of lines in a method (some people
say over 5 to 10 is too many) - Causes
- Doing more than one cohesive thing in a method
- Refactoring
- Extract Method to break up method to smaller
methods
25Long Method Example
- public static void report(Writer out, List
machines, Robot robot) - throws IOException
- out.write("FACTORY REPORT\n")
- Iterator line machines.iterator()
- while (line.hasNext())
- Machine machine (Machine) line.next()
- out.write("Machine " machine.name())
- if (machine.bin() ! null)
- out.write(" bin" machine.bin())
- out.write("\n")
-
- out.write("\n")
- out.write("Robot")
- if (robot.location() ! null)
- out.write(" location"
robot.location().name()) - if (robot.bin() ! null)
- out.write(" bin" robot.bin())
- out.write("\n")
- out.write("\n")
26Long Method Example
- public static void report(Writer out, List
machines, Robot robot) - throws IOException
- out.write("FACTORY REPORT\n")
- Iterator line machines.iterator()
- while (line.hasNext())
- Machine machine (Machine) line.next()
- out.write("Machine " machine.name())
- if (machine.bin() ! null)
- out.write(" bin" machine.bin())
- out.write("\n")
-
- out.write("\n")
- out.write("Robot")
- if (robot.location() ! null)
- out.write(" location"
robot.location().name()) - if (robot.bin() ! null)
- out.write(" bin" robot.bin())
- out.write("\n")
- out.write("\n")
27Long Method Example
- public static void report(Writer out, List
machines, Robot robot) throws IOException - reportHeader(out)
- reportMachines(out, machines)
- reportRobot(out, robot)
- reportFooter(out)
28Long Parameter List
- Symptoms
- A method has many parameters (some say more than
one or two) - Causes
- Attempt to minimise coupling
- Genralised algorithm
- Refactoring
- If parameter can be obtained from another object
already known to this one, Replace Parameter with
Method. - If the parameters come from a single object, try
Preserve Whole Object - If the data is not from one logical object, maybe
group them with Introduced Parameter
29Long Parameter List - Example
- public void paintComponent(Graphics gr, Component
renderer, Container parent, int x, int y, int
width, int height, Boolean shouldValidate) - Introduce Parameter Object
- public void paintComponent(Graphics gr, Component
renderer, Container parent, Rectangle rect,
Boolean shouldValidate)
30Replace Temp with Query
- Symptoms
- Using temporary variable to hold result of
calculation (expression) - Refactoring
- Extract expression to method and replace all
references to temp with call to method.
31Replace Temp with Query e.g.
- public double correlation(double x, double
y, int len) - ...
- double sumProd sumProd(x,y,len)
- double sumSquaresX sumSquares(x,len)
- double sumSquaresY sumSquares(y,len)
- ...
- return (lensumProd sumXsumY) /
- (squareRoot( (lensumSquareX square(sumX))
- (lensumSquareY square(sumY) ))
32Replace Temp with Query - Refactoring
public double correlation(double x, double
y, int len) return (lensumProd(x,y,len)
sum(x,len)sum(y,len)) / (squareRoot(
(lensumSquare(x,len)- square(sum(x,len)))
(lensumSquare(y,len)- square(sum(y,len)))
))
33Move Method
- Symptoms
- A method is using more features (attributes and
operations) of another class than the class on
which it is defined - Causes
- Often caused as a result of other refactoring
- Refactoring
- Create a new method with a similar body in the
class it uses most. - Either turn the old method into a simple
delegation, or remove it altogether
34Move Method Example
- public class Report
- public static void report(Writer out, List
machines, Robot robot) - throws IOException
- reportHeader(out)
- reportMachines(out, machines)
- reportRobot(out, robot)
- reportFooter(out)
-
- ...
- private void reportRobot(Writer out, Robot
robot) - out.write("Robot")
- if (robot.location() ! null)
- out.write(" location"
robot.location().name()) - if (robot.bin() ! null)
- out.write(" bin" robot.bin())
- out.write("\n")
-
35Move Method Example
- Move method to Robot Class
- public class Robot
- ...
- public void reportRobot(Writer out)
- out.write("Robot")
- if (location() ! null)
- out.write(" location"
- location().name())
- if (bin() ! null)
- out.write(" bin" bin())
- out.write("\n")
-
- ...
36Move Method Example
- Change call in client
- public class Report
- public static void report(Writer out, List
machines, Robot robot)throws IOException - reportHeader(out)
- reportMachines(out, machines)
- robot.reportRobot(out)
- reportFooter(out)
-
- ...
- private void reportRobot(Writer out, Robot
robot) - ...
37Move Method Example
- Remove method from client
- public class Report
- public static void report(Writer out, List
machines, Robot robot) throws IOException - reportHeader(out)
- reportMachines(out, machines)
- robot.reportRobot(out)
- reportFooter(out)
-
38Replace Conditional withPolymorphism
- Symptoms
- You have a conditional that chooses different
behavior depending on the type of an object - Refactoring
- Move each leg of the conditional to an
overriding method in a subclass. - Make the original method abstract
39Refactoring
- Get rid of the switch statement
- class Movie double getCharge( int
daysRented ) double result 0
switch (getPriceCode()) case
Movie.REGULAR result 2
if (daysRented gt 2)
result (daysRented - 2) 1.5
break case Movie.NEW_RELEASE
result daysRented 3
break case Movie.CHILDRENS
result 1.5 if
(daysRented gt 3) result
(daysRented - 3) 1.5 break
return result
40Refactoring
41Refactoring
- Flaw
- A movie may change its classification during its
lifetime. - An object cannot change its class during its
lifetime.
42Refactoring
- Solution
- Use State design pattern.
43(No Transcript)
44Refactoring
- Replace price (type) code
- Apply Replace Type Code with State.
- Compile and test after each step.
45Refactoring
- 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
46Refactoring
- 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
47Refactoring
- Add new state classesabstract class Price
public abstract int getPriceCode()class
RegularPrice extends Price public int
getPriceCode() return Movie.REGULAR
class NewReleasePrice extends Price
public int getPriceCode() return
Movie.NEW_RELEASE class ChildrensPrice
extends Price public int getPriceCode()
return Movie.CHILDRENS
48Refactoring
- Replace price type codes with instances of price
state classes
49Refactoring
- public class Movie private int
_priceCode public int getPriceCode()
return _priceCode public
void setPriceCode( int arg ) _priceCode
arg
50- 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 )
51- class Movie double getCharge( int
daysRented ) double result 0
switch (getPriceCode()) case
Movie.REGULAR result 2
if (daysRented gt 2)
result (daysRented - 2) 1.5
break case Movie.NEW_RELEASE
result daysRented 3
break case Movie.CHILDRENS
result 1.5 if
(daysRented gt 3) result
(daysRented - 3) 1.5 break
return result
52Refactoring
- Move getCharge() to Price class
- Apply Move Method.
53- class Price double getCharge( int
daysRented ) double result 0
switch (getPriceCode()) case
Movie.REGULAR result 2
if (daysRented gt 2)
result (daysRented - 2) 1.5
break case Movie.NEW_RELEASE
result daysRented 3
break case Movie.CHILDRENS
result 1.5 if
(daysRented gt 3) result
(daysRented - 3) 1.5 break
return result
54- class Movie double getCharge( int
daysRented ) return _price.getCharge(
daysRented )
55- class Price double getCharge( int
daysRented ) double result 0
switch (getPriceCode()) case
Movie.REGULAR result 2
if (daysRented gt 2)
result (daysRented - 2) 1.5
break case Movie.NEW_RELEASE
result daysRented 3
break case Movie.CHILDRENS
result 1.5 if
(daysRented gt 3) result
(daysRented - 3) 1.5 break
return result
56Refactoring
- Replace switch statement in getCharge()
- For each case, add overriding method.
- Define abstract method.
- Apply Replace Conditional with Polymorphism.
57- class RegularPrice double getCharge( int
daysRented ) double result 2
if (daysRented gt 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 gt 3)
result (daysRented - 3) 1.5
58- class Price abstract double
getCharge( int daysRented )
59Refactoring
- Replace if statement in getFrequentRenterPoints()
- Apply Replace Conditional with Polymorphism.
60Refactoring
- class Price int getFrequentRenterPoin
ts( int daysRented ) if
((getPriceCode() Movie.NEW_RELEASE)
daysRented gt 1) return 2
else return 1
61Refactoring
- class Price int getFrequentRenterPoin
ts( int daysRented ) return 1
class NewReleasePrice int
getFrequentRenterPoints( int daysRented )
return (daysRented gt 1) ? 2 1
62Refactoring
- 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
63Refactoring
- Benefits of second refactoring
- easy to change movie classifications
- easy to change rules for charging and frequent
renter points
64(No Transcript)
65Refactoring
66References
- Refactoring
- M. Fowler et al.
- Addison-Wesley, 1999
- Refactoring Workbook
- W. Wake
- Addison-Wesley, 2003
- http//xp123.com/rwb/ - Sample chapter, exercises
and sample code that needs refactoring