Refactoring

nutmegactΛογισμικό & κατασκευή λογ/κού

10 Νοε 2012 (πριν από 5 χρόνια και 7 μέρες)

219 εμφανίσεις

Refactoring

What is Refactoring?


Refactoring is a disciplined technique for
restructuring an existing body of code,

altering its internal structure without
changing its external behavior.


Its heart is a series of small behavior preserving trans
-
formations. Each transformation (called a 'refactoring') does
little, but a sequence of transformations can produce a
significant restructuring. Since each refactoring is small, it's less
likely to go wrong. The system is also kept fully working after
each small refactoring, reducing the chances that a system can
get seriously broken during the restructuring.”

http://refactoring.com

What Motivates Us to Refactor?


Make it
easier to add new code



Improve the design

of existing code



Gain a
better understanding

of code



Make coding
less annoying





[Kerievsky]

What Alerts Us to Refactor?


Code Smells


Signs that there might be something ugly in the
design waiting to be refactored away



Examples:


Long Method


Long Parameter List


Duplicate Code


Large Class


Comments


Feature Envy


Middle Man


Message Chains


Shotgun Surgery

Something to think about


The longer you wait before paying your debt,
the bigger the bill


common sense



The bigger the mess, the less you want to clean
it up


Joshua Kerievsky



Perfection is reached not when there remains
nothing to add, but when there remains
nothing to remove



Antoine de Saint
-
Exupéry,
1900
-
1944

Metaphor: Clean As You Go

Mrs. Beeton
The Book of Household
Management

CLEAN AS YOU GO

Quoted by Kevlin Henney and

James Newkirk at JAOO 2004,

featured in Joshua Kerievsky’s

Refactoring to Patterns

What is “mercilessly”?

“The bigger the mess, the less you want to clean it up”



Joshua
Kerievsky



Continuously.


Refactor as often as you possibly can


If you decide to leave a particular refactoring for later, know the
cost

of
doing so!


Before or after. Not during.


Keep focused. You’ll do less mistakes and it’s easier to keep your
head around things that way.


What happens if you don’t refactor?




you incur
Design Debt



Design Debt refers to the unwanted consequences of letting
sloppy design and programming exist in the codebase


it’s
the software design equivalent for Technical Debt




Workarounds instead of “form fit” additions



Over
-
engineering is Design Debt as well!


[Cunningham]

Design Debt

Design Income?


If there’s Design Debt, then is there equivalent

“Design Income” in the other end of the scale?



Up
-
front design could be considered Design Income.
However, there’s a big risk of the potential Design
Income becoming Design Debt because of over
-
engineering and wrongful assumptions.



Due to this risk, up
-
front design should only be done
for “sure things”. Then again, “sure things” are rarely
big winners, so…




Up
-
front design is often a bad investment


Refactoring and Design
Patterns


Design Patterns are closely related to
Refactoring:


Refactorings are transformations from one state
to another


These states are often Design Patterns!







[Alexander], [GoF], [Hillside]

Preventing Regression


Unit tests


A good enough coverage lets us refactor with
confidence that nothing was broken.


Automated tests get run. Manual tests don’t.


Functional tests


For the random situations where unit tests aren’t
sufficient for preventing regression


Automated tests get run. Manual tests don’t.



Side
-
track: Test
-
Driven Development


Rewriting vs. Refactoring


Rewriting != Refactoring


The word “refactoring” is often used in the context of a plain
rewrite of existing code, which is most often a blatant mistake


…however…



Refactoring ~ Rewriting


Some refactorings can be thought of as controlled rewrites in
well
-
defined, small steps


…and sometimes…



Refactoring ~ Deleting


Deleting code can boost a team’s performance

Metaphor: John Thompson

(from
Refactoring to Patterns
)

John Thompson, hatter, makes

and sells hats for ready money.

Metaphor: John Thompson
(cont’d)

John Thompson

Next Up…

… a bunch of sample refactorings in Java

Refactorings

Rename Method


Mechanics:


1.
Check to see whether the method signature is implemented by a super class or a subclass. If it
is, these steps must be performed for each implementation.


2.
Declare a new method with the new name. Copy the old body of code over to the new name
and make any alterations to fit.


3.
Compile.


4.
Change the body of the old method so that it calls the new one.


5.
Compile and test.


6.
Find all references to the old method and change them to call the new one instead. Compile
and test after each change.


7.
Remove the old method.


8.
Compile and test.

Exercise 1
-
4
-
1:

Rename Method


Task:


Rename the setAmount(float) method in
example.Money to setValue(float)

without
using Eclipse’s refactoring wizard, relying on the
steps described in
exercise
-
1.4
-
1
.txt

Refactorings

Rename Method (cont’d)


Observations


Lot of work for a small refactoring, and


Very mechanical work, but also


Easy to automate


…which leads to this particular refactoring (and
many others, of course) being well
-
supported
by modern IDE’s such as Eclipse.

Refactorings

Pull Up Method


A slightly more complex refactoring involving inheritance


Mechanics:


Inspect the methods to ensure they are identical (yes, identical


if they aren’t, use
Substitute Algorithm to make them identical)


If the methods have different signatures, change the signatures to the one you want to use
eventually in the super class


Create a new method in the super class, copy the body of one of the methods to it, adjust,
and compile. (you might need to Pull Up Field, first)


Delete one subclass method.


Compile and test.


Keep deleting subclass methods one at a time, testing until only the super class method
remains.


Check if you can change any callers to refer to the super type instead.

Exercise
1
-
4
-
2
:

Pull Up Field & Pull Up Method


Task:


Pull Up Field ‘name’

without using Eclipse’s
refactoring wizard, relying on the steps
described in
exercise
-
1.4
-
2
.txt


Pull Up Method ‘getName()’

without using
Eclipse’s refactoring wizard, relying on the steps
described in
exercise
-
1.4
-
2
.txt

Refactorings

Pull Up Method (cont’d)


Observations


Uses other, lower
-
level refactorings to
accomplish a slightly bigger refactoring


Clearly more complex, but still feasible to
automate

Refactorings

Introduce Null Object


Again, a slightly more complex refactoring.


Mechanics:


Create a subclass of the source class to act as a NullObject.


Create an isNull() method on the source class and the null class


for the source class the
method should return false, for the null class it should return true.


Compile.


Find all places that can return a null when asked for a source object. Replace them to give
out an instance of the null class instead. Compile and test after each change.


Find all places that compare a variable of the source type with null and replace them with a
call to isNull(). Compile and test after each change.


Look for cases in which client code invokes a method if not null and do some alternative
behavior if null. For each of these cases override the method in question in the null class
with the alternative behavior originally implemented in client code. Remove the condition
check for those that use the overridden behavior.


Compile and test.

if (customer == null) {


plan = BillingPlan.basic();

} else {


plan = customer.getPlan();

}

Exercise 1
-
4
-
3:

Introduce Null Object


Task:


Introduce a Null Object

to replace the null
-
check in the PricingMachine class, relying on
the steps described in
exercise
-
1.4
-
3
.txt

Refactorings

Introduce Null Object (cont’d)


Observations


Transformation towards a design pattern


Seems difficult to automate


Has the potential of cleaning up significant
amounts of error
-
prone, distracting code

Refactorings

Replace Inheritance with Delegation


This doesn’t look too difficult, does it?


Mechanics:


Create a field in the subclass that refers to an instance of the super class. Initialize the
field to ‘this’.


Change each method defined in the subclass to use the delegate field. Compile and test
after changing each method.


Remove the subclass declaration and replace the delegate assignment with an
assignment to a new object of the ex
-
super class type.


For each super class method, add a simple delegating method to the ex
-
subclass.


Compile and test.

Exercise 1
-
4
-
4:

Replace Inheritance with Delegation


Task:


Replace Inheritance with Delegation

in the
CollectionOfItems class, relying on the steps
described in
exercise
-
1.4
-
4.txt

Refactorings

Replace Inheritance with Delegation
(cont’d)


Observations


A relatively simple refactoring, clearly less
complex than
Introduce Null Object


…so what’s the big deal?

Refactorings

Replace Delegation with Inheritance


This is exactly the reverse of “Replace Inheritance with Delegation!”


Mechanics:


Make the delegating object a subclass of the delegate.


Compile. Use Rename Method to fix any name clashes between the two classes’
methods.


Set the delegate field to be the object itself.


Remove the simple delegation methods.


Compile and test.


Replace all other delegations with calls to the object itself.


Remove the delegate field.

Exercise
1
-
4
-
5
:

Replace Delegation with Inheritance


Task:


Replace Delegation with Inheritance

in the
CollectionOfItems class, relying on the steps
described in
exercise
-
1.4
-
5.txt

Refactorings

Replace Delegation with Inheritance


Observations


Exact reverse of
Replace Inheritance with
Delegation


Refactorings are often like this


a nice side
-
effect of
being well
-
defined…

Refactorings

Replace Type Code …

…with Subclasses

…with Strategy

There’s no one
-
to
-
one mapping between code smells and solutions.
Sometimes multiple refactorings provide slightly varying ways to climb out
of the same hole…

My Favorite Refactorings


There are some refactorings that one uses more than others. For
me, the following are probably the ones I use the most:


Rename Method


Extract Method


Move Method


Rename Class


Extract Class


Inline Temp


Extract Constant



Low
-
level refactorings; thus encountered more often than e.g.
Introduce Null Object

and thus better supported by IDE’s.

Tool Support


JAVA


Eclipse


IntelliJ IDEA


Borland JBuilder


NetBeans


RefactorIT plug
-
in for Eclipse, Borland JBuilder,
NetBeans, Oracle JDeveloper


.NET


IntelliJ Resharper for Visual Studio .NET


Visual Studio .NET Team Studio 2005
(Forthcoming)