Refactoring
Presentation for:
South Florida PHP Users Group
By:
Adam Culp
Twitter: @adamculp
http://www.geekyboy.com
Refactoring
Based on popular “Refactoring” Improving The
Design of Existing Code book, by Martin
Fowler.
We will start in the code with an example.
Tips and descriptions will be handled during the
example.
In the example we are tasked with creating an
HTML representation of the customer
statement for a movie rental.
Refactoring
Movie class
With a new project we take
a look at the current
application a customer has
requested to be changed.
We start with the Movie
class.
Refactoring
Rental class
Next we look at the Rental
class.
Refactoring
Customer class p1
With a new project we take
a look at the current app
that is about to be
changed.
Next we look at part 1 of
the Customer class.
Refactoring
Customer class p2
With a new project we take
a look at the current app
that is about to be
changed.
Next we look at part 2 of
the Customer class.
Refactoring
Not terrible for a small quick and dirty application, but
if part of a larger application we have problems.
Customer class doing far too much, and statement
method is too long.
Impossible to reuse the statement for an HTML
representation.
The customer also wants to be able to change how
they classify movies.
As experienced developers we know that customers
will always come back six months from now and want
it changed in some way.
Refactoring
Rewrite vs Refactor
Budget constraint
Time constraint
Rewrite is an excuse to NOT dig in someone's code
Refactor is the best teacher
Refactoring
First step
Build tests based on current “working” application,
so we can verify if we break something.
Pre-load database with data or create fixtures
needed for tests, to ensure we have a constant to
compare test results.
If we are not starting with a working application you
must first get the application working prior to
refactoring. Do not try to refactor a broken
application or you risk breaking it further, and end
up with a total rewrite.
Refactoring
Step 1
Extract Method to move switch statement to its own method.
Refactoring
Step 2
Variable names should make sense. (each > rental, thisAmount > result)
Refactoring
Step 3
Extract Method amountFor() to Rental class. It uses information from
Rental, but no information from Customer. We rename it to more
meaningful getCharge().
Refactoring
Step 4
Call getCharge() directly from statement() instead of using amountFor() as
a middle man method.
Refactoring
Step 5
Replace Temp with Query - Cleanup of call to getCharge(). Eliminate
temp variables by calling method direct. Less maintenance.
Refactoring
Step 6
Extract Method and move frequentRenterPoints to its own method in
Rental class where it logically belongs.
Refactoring
Step 7
We do Replace Temp with Query for totalAmount. Temp variables can be
a problem and really only serve a purpose within their own routine and
encourage long, complex routines.
Refactoring
Step 8
Also do Replace Temp with Query on frequentRentalPoints to eliminate
more temp variables.
Refactoring
Step 9
We are now able to create the HTML version of the statement, and we
rename the original to represent a Text version.
Refactoring
Recap
Most refactoring reduces code, while we increased it for
this example.
We also duplicated a loop to make it happen 4 times
instead of once. (getFrequentRenterPoints,
getTotalCharge, getTotalFrequentRenterPoints)
Optimizing and refactoring are different actions.
Refactor first, then optimize later if needed.
More refactoring could further clean up Text and HTML
statements to DRY it up. (split out header/footer, etc.)
Still need more refactoring to allow classification
changes by the customer.
Refactoring
Step 10
Move Method on getCharge() because it uses data in Movie, so should be
in the same class as the data.
Refactoring
Step 11
Replace Conditional with Polymorphism on getCharge() because it uses
data in Movie, so should be in the same class as the data.
Refactoring
Two hats
Adding Function Hat
Refactoring Hat
First we add functionality, they we refactor it, then
add more functionality, ...
Refactoring
Why Refactor?
Without refactoring code decays
As code is changed it loses structure, making it
harder to see design
Regular refactoring preserves design
Poorly designed = more code due to duplication
Reduced code = easier to maintain
Reduced code = easier to understand
Helps find bugs
Helps us program faster
Refactoring
When to Refactor?
Should not set aside time to refactor, it is just
something we do in short bursts
Refactor because you want to do something, and
refactoring helps you do it
Often a quick refactor can help developing new
functionalities move faster
Rule of Three → see next slide
Refactoring
Rule of Three
When you add function
Helps to learn code you need to modify
Changes code that prevents the addition
When you need to fix a bug
Make code more understandable
Usually highlights the bug
During code review
Code looks good to developer, but maybe not to
the team.
More concrete results
Refactoring
What do I tell my manager?
For a tech saavy manager it may not be hard to
explain the benefits
Quality centric manager stress quality aspects
Perhaps introduce it as a review process
There are many resources on Google about
value of reviews, inspections, or software
development process
Schedule driven … Don't tell (controversial?)
Find a way to work it into scheduling.
Overall it saves time, but some will never “see” it
Refactoring
Code “smells” as indicators
Bad code “smells” and is a great way of indicating it
is time for a refactor.
Duplicate Code
Long Method
Large Class
Long Parameter (argument) List
Divergent Change - if change is needed in
multiple areas to accommodate a change
Shotgun Surgery – change causes ripple of other
needed changes
Feature Envy – a method seems to use another
class instead of the one it is in
Refactoring
Code “smells” as indicators
More “smells”
Data Clumps – if data items tend to accompany
one another
Primitive Obsession
Switch statements
Parallel Inheritance Hierarchies – caused when
you need to create a subclass of one object
because you created a subclass of another
Lazy Class – classes that do not do much and do
not pay for their extra weight
Speculative Generality – constructs built for the
sake of possibility later
Refactoring
Code “smells” as indicators
More “smells”
Temporary Field
Message Chains – object asking for object that
asks for another object...
Middle Man – directors in place but serving no
real purpose
Inappropriate Intimacy – classes should not deal
too much with each others private parts
Data Class – getters and setters, but nothing else
Comments – where comments are used as
deodorant to cover a bad smell
Refactoring
Tests and Refactoring
You cannot properly refactor without tests in place.
Writing tests is the first step of refactoring, and
should happen as you write the code in the first
place. (or before, as with TDD)
Refactoring
Thank you
Adam Culp
http://www.geekyboy.com
http://github.com/adamculp
Twitter @adamculp