Title: Refactoring
1Refactoring
- Overview
- What is refactoring?
- A catalogue of bad smells
2Refactoring
- Basic metaphor
- Start with an existing code base and make it
better. - Change the internal structure (in-the-small to
in-the-medium) while preserving the overall
semantics - i.e., rearrange the factors but end up with the
same final product - The idea is that you should improve the code in
some significant way. For example - Reducing near-duplicate code
- Improved cohesion, lessened coupling
- Improved parameterization, understandability,
maintainability, flexibility, abstraction,
efficiency, etc
3What Is Refactoring?
- Definition
- The process of changing software in a way that
the external behavior is not alteredbut the
internal structure is improved. - Or, an instance of such a change.(E.g. I
carried out a refactoring.) - Cleaning up code. Improving design.But after
the code works. - Fights against software entropy
4Why Refactor?
- Continuous improvement of design
- Tidying up. Avoids decay.
- Needed if make it work is first priority
- Best design choices may not evident at first
- Makes software easier to understand
- Refactoring done to clean up hacks
- Done after reflection.
- May be done to remove ambiguity, misleading code,
a hack, etc. - If you can change it successfully, you understand
it
5Why Refactor? (2)
- Helps you find bugs
- A change to improve structure may reveal a flaw
in old implementation - Helps you developing working code more quickly
- Counter-intuitive!
- Short cycles of add-functionality then
improve-design. (Kent Becks two hats idea).
6But Hold On
- Possible objections.
- Touching the design is asking for trouble!
- Once its working, why bother?
- After I think its working, dont I have to
re-verify the design changes again? - What we need to make refactoring successful is
- Unit tests
7Refactoring
- Reference
- Refactoring Improving the Design of Existing
Code, - by Martin Fowler (et al.),1999, Addison-Wesley
- Fowler, Beck, et al. are big wheels in the OOAD
crowds - Smalltalk experience
- OO design patterns
- XP (extreme programming)
8Refactoring
- Book is very good
- Ideas have been around for a long time tho.
- Experienced OO programmers will have a pretty
good handle on most of the ideas - Mostly its a catalogue of transformations that
you can perform on your code, with motivation,
explanation, variations, and examples - e.g., Extract interface, Move method, Pull up
constructor body - Refactorings often come as duals
- e.g., Replace inheritance with delegation and
- Replace delegation with inheritance
9Refactoring
- Book has a catalogue of
- 22 bad smells
- i.e., things to look out for, anti-patterns
- This font and colour indicates the name of a bad
smell. - 72 refactorings
- i.e., what to do when you find them
- This font and colour indicates the name of a
refactoring. - As with GoF book on OODPs, there is overlap
between the catalogue items, and the ideas start
to blur. - We will look at some of the bad smells and what
to do about them.
10Some advice from Fowler
- When should I refactor? How often?
- How much time should I dedicate to it?
- Its not something you should dedicate two weeks
for every six months - rather, you should do it as you develop!
- Refactor when you recognize a warning sign (a
bad smell) and know what to do - when you add a function
- Likely its not an island unto itself
- when you fix a bug
- Is the bug symptomatic of a design flaw?
- when you do a code review
- A good excuse to re-evaluate your designs, share
opinions.
11When Should Refactor?
- Remember afterwards! (After what?)
- The Rule of Three
- First, just do it. Second time, do it badly but
tolerate it. Third time, do it well refactor. - Some guidelines
- When you need to add a function. Existing
design makes this hard. Improve it. - When you need to fix a bug.
- As part of a code review.
12The rule of three (XP)
- The first time you code a task, just do it.
- The second time you code the same idea, wince and
code it up again. - The third time you code the same idea, its time
to refactor! - Any programming construct can be made more
abstract but thats not necessarily a good
thing. - Generality (flexibility) costs too
- Dont spin you wheels designing and coding the
most abstract system you can imagine. - Practise Just-in-Time abstraction.
- Expect that you will be re-arranging your code
constantly. Dont worry about it. Embrace it.
13Refactoring and Design Smells
- Fowler, design smells, and catalog of
refactorings - The book
- http//www.refactoring.com/
- List of smells on the webhttp//wiki.java.net/bi
n/view/People/SmellsToRefactorings
14Bad smells in code
- Duplicated code
- The 1 bad smell
- Same expression in two methods in the same class?
- Make it a private ancillary routine and
parameterize it - (Extract method)
- Same code in two related classes?
- Push commonalities into closest mutual ancestor
and parameterize - Use template method DP for variation in subtasks
- (Form template method)
15Bad smells in code
- Duplicated code
- Same code in two unrelated classes?
- Ought they be related?
- Introduce abstract parent (Extract class, Pull up
method) - Does the code really belongs to just one class?
- Make the other class into a client (Extract
method) - Can you separate out the commonalities into a
subpart or a functor or other function object? - Make the method into a subobject of both classes.
- Strategy DP allows for polymorphic variation of
methods-as-objects - (Replace method with method object)
16Bad smells in code
- Long method
- Often a sign of
- Trying to do too many things
- Poorly thought out abstractions and boundaries
- Micromanagement anti-pattern
- Best to think carefully about the major tasks and
how they inter-relate. Be aggressive! - Break up into smaller private methods within the
class - (Extract method)
- Delegate subtasks to subobjects that know best
(i.e., template method DP) - (Extract class/method, Replace data value with
object)
17Bad smells in code
- Long method
- Fowlers heuristic
- When you see a comment, make a method.
- Often, a comment indicates
- The next major step
- Something non-obvious whose details detract from
the clarity of the routine as a whole. - In either case, this is a good spot to break it
up.
18Bad smells in code
- Large class
- i.e., too many different subparts and methods
- Two step solution
- Gather up the little pieces into aggregate
subparts. - (Extract class, replace data value with object)
- Delegate methods to the new subparts.
- (Extract method)
- Likely, youll notice some unnecessary subparts
that have been hiding in the forest! - Resist the urge to micromanage the subparts!
19Bad smells in code
- Large class
- Counter example
- Library classes often have large, fat interfaces
(many methods, many parameters, lots of
overloading) - If the many methods exist for the purpose of
flexibility, thats OK in a library class.
20Bad smells in code
- Long parameter list
- Long parameter lists make methods difficult for
clients to understand - This is often a symptom of
- Trying to do too much
- too far from home
- with too many disparate subparts
21Bad smells in code
- Long parameter list
- In the old days, structured programming taught
the use of parameterization as a cure for global
variables. - With modules/OOP, objects have mini-islands of
state that can be reasonably treated as global
to the methods (yet are still hidden from the
rest of the program). - i.e., You dont need to pass a subpart of
yourself as a parameter to one of your own
methods.
22Bad smells in code
- Long parameter list
- Solution
- Trying to do too much?
- Break up into sub-tasks
- (Extract method)
- too far from home?
- Localize passing of parameters dont blithely
pass down several layers of calls - (Preserve whole object, introduce parameter
object) - with too many disparate subparts?
- Gather up parameters into aggregate subparts
- Your method interfaces will be much easier to
understand! - (Preserve whole object, introduce parameter
object)
23Bad smells in code
- Divergent change
- Occurs when one class is commonly changed in
different ways for different reasons - Likely, this class is trying to do too much and
contains too many unrelated subparts - Over time, some classes develop a God complex
- They acquires details/ownership of subparts that
rightly belong elsewhere - This is a sign of poor cohesion
- Unrelated elements in the same container
- Solution
- Break it up, reshuffle, reconsider relationships
and responsibilities (Extract class)
24Bad smells in code
- Shotgun surgery
- the opposite of divergent change
- Each time you want to make a single, seemingly
coherent change, you have to change lots of
classes in little ways - Also a classic sign of poor cohesion
- Related elements are not in the same container!
- Solution
- Look to do some gathering, either in a new or
existing class. - (Move method/field)
25Bad smells in code
- Feature envy
- A method seems more interested in another class
than the one its defined in - e.g., a method Am() calls lots of get/set
methods of class B - Solution
- Move m() (or part of it) into B!
- (Move method/field, extract method)
- Exceptions
- Visitor/iterator/strategy DP where the whole
point is to decouple the data from the algorithm - Feature envy is more of an issue when both A and
B have interesting data
26Bad smells in code
- Data clumps
- You see a set of variables that seem to hang
out together - e.g., passed as parameters, changed/accessed at
the same time - Usually, this means that theres a coherent
subobject just waiting to be recognized and
encapsulated
void ScenesetTitle (string titleText, int
titleX, int titleY, Colour titleColour) void
ScenegetTitle (string titleText, int
titleX, int titleY, Colour titleColour)
27Bad smells in code
- Data clumps
- In the example, a Title class is dying to be born
- If a client knows how to change a titles x, y,
text, and colour, then it knows enough to be able
to roll its own Title objects. - However, this does mean that the client now has
to talk to another class. - This will greatly shorten and simplify your
parameter lists (which aids understanding) and
makes your class conceptually simpler too. - Moving the data may create feature envy initially
- May have to iterate on the design until it feels
right. - (Preserve whole object, extract class, introduce
parameter object)
28Bad smells in code
- Primitive obsession
- All subparts of an object are instances of
primitive types - (int, string, bool, double, etc.)
- e.g., dates, currency, SIN, tel., ISBN, special
string values - Often, these small objects have interesting and
non-trivial constraints that can be modelled - e.g., fixed number of digits/chars, check digits,
special values - Solution
- Create some small classes that can validate and
enforce the constraints. - This makes your system mode strongly typed.
- (Replace data value with object, extract class,
introduce parameter object)
29Bad smells in code
- Switch statements
- We saw this before heres Fowlers example
Double getSpeed () switch (_type) case
EUROPEAN return getBaseSpeed() case
AFRICAN return getBaseSpeed()
getLoadFactor() _numCoconuts case
NORWEGIAN_BLUE return (_isNailed) ? 0
getBaseSpeed(_voltage)
30Bad smells in code
- Switch statements
- This is an example of a lack of understanding
polymorphism and a lack of encapsulation. - Solution
- Redesign as a polymorphic method of PythonBird
- (Replace conditional with polymorphism, replace
type code with subclasses)
31Bad smells in code
- Lazy class
- Classes that doesnt do much thats different
from other classes. - If there are several sibling classes that dont
exhibit polymorphic behavioural differences , the
consider just collapsing them back into the
parent and add some parameters - Often, lazy classes are legacies of ambitious
design or a refactoring that gutted the class of
interesting behaviour - (Collapse hierarchy, inline class)
32Bad smells in code
- Speculative generality
- We might need this one day
- Fair enough, but did you really need it after
all? - Extra classes and features add to complexity.
- XP philosophy
- As simple as possible but no simpler.
- Rule of three.
- Keep in mind that refactoring is an ongoing
process. - If you really do need it later, you can add it
back in. - (Collapse hierarchy, inline class, remove
parameter)
33Bad smells in code
- Message chains
- Client asks an object which asks a subobject,
which asks a subobject, - Multi-layer drill down may result in
sub-sub-sub-objects being passed back to
requesting client. - Sounds like the client already has an
understanding of the structure of the object,even
if it is going through appropriate
intermediaries. - Probably need to rethink abstraction
- Why is a deeply nested subpart surfacing?
- Why is the subpart so simple that its useful far
from home? - (Hide delegate)
34Bad smells in code
- Middle man
- All hard problems in software engineering can be
solved by an extra level of indirection. - OODPs pretty well all boil down to this, albeit
in quite clever and elegant ways. - If you notice that many of a classs methods just
turn around and beg services of delegate
subobjects, the basic abstraction is probably
poorly thought out. - An object should be more than the some of its
parts in terms of behaviours! - (Remove middle man, replace delegation with
inheritance)
35Bad smells in code
- Inappropriate intimacy
- Sharing of secrets between classes, esp. outside
of the holy bounds of inheritance - e.g., public variables, indiscriminate
definitions of get/set methods, C friendship,
protected data in classes - Leads to data coupling, intimate knowledge of
internal structures and implementation decisions. - Makes clients brittle, hard to evolve, easy to
break. - Solution
- Appropriate use of get/set methods
- Rethink basic abstraction.
- Merge classes if you discover true love
- (Move/extract method/field, change bidirectional
association to unidirectional, hide delegate)
36Bad smells in code
- Alternative classes with different interfaces
- Classes/methods seem to implement the same or
similar abstraction yet are otherwise unrelated. - This is not a knock against overloading, just
haphazard design. - Solution
- Move the classes closer together.
- Find a common interface, perhaps an ABC.
- Find a common subpart and remove it.
- (Extract superclass, move method/field, rename
method)
37Bad smells in code
- Data class
- Class consists of (simple) data fields and simple
accessor/mutator methods only. - Often, youll find that clients of this class are
using get/set methods just like the micromanager
anti-pattern (albeit via a level of indirection). - Solution
- Have a look at usage patterns in the clients
- Try to abstract some commonalities of usage into
methods of the data class and move some
functionality over - My own view is that data classes are quite
reasonable, if used judiciously. - In C, often use structs to model data classes.
- Data classes are like children. They are OK as
a starting point, but to participate as a grownup
object, they need to take on some
responsibility. - (Extract/move method)
38Bad smells in code
- Refused bequest
- Subclass inherits methods/variables but doesnt
seem to use some of them. - In a sense, this might be a good sign
- The parent manages the commonalities and the
child manages the differences. - Might want to look at typical client use to see
if clients think child is-a parent - Do clients use parents methods? use parent as
static type? - Did the subclass inherit as a cheap pickup of
functionality? - Fowler/Beck claim this isnt as bad a smell as
the others - Might be better to use delegation
- (Replace inheritance with delegation)
39Bad smells in code
- Refused bequest
- Another perspective
- Parent has features that are used by only some of
its children. - Typical solution is to create some more
intermediate abstract classes in the hierarchy. - Move the peculiar methods down a level.
- (Push down field/method)
40Bad smells in code
- Comments
- XP philosophy discourages comments, in general
- Instead, make methods short and use long
identifiers - Fowler is not so critical, tho.
- In the context of refactoring, Fowler claims that
long comments are often a sign of opaque,
complicated, inscrutable code. - They arent against comments so much as in favour
of self-evident coding practices. - Rather than explaining opaque code, restructure
it! - (Extract method/class, many others applicable
) - Comments are best used to document rationale
- i.e., explain why you picked one approach over
another.
41Unit Testing in TDD
- Motto Clean code that works. (Ron Jeffries)
- Unit testing has broader goals that just
insuring quality - Improve developers lives (coping, confidence)
- Support design flexibility and change
- Allow iterative development with working code
early
42Unit Testing Benefits
- Developers can work in a predictable way of
developing code - Programmers write their own unit tests
- Get rapid response for testing small changes
- Build many highly-cohesive loosely-coupled
modules to make unit testing easier
43Red/Green/Refactor
- The TDD mantra of how to code
- Red write a little test that doesnt work,
perhaps even doesnt compile - Green Write code to make the test work quickly
(perhaps not the best code) - Refactor Eliminate duplication and other
problems that you did to just make the test work
44Summary
- Fowler et al.s Refactoring is a well-written
book that summarizes a lot of best practices of
OOD/OOP with nice examples. - Many books on OOD heuristics are vague and lack
concrete advice. - Most of the advice in this book is aimed at
low-level OO programming. - i.e., loops, variables, method calls, and class
definitions. - Next obvious step up in abstraction/scale is to
OODPs. - i.e., collaborating classes
- This is an excellent book for the
intermediate-level OO programmer. - Experienced OO programmers will have discovered a
lot of the techniques already on their own.
45Tool Support for Refactoring
- Refactorings standard re-structurings to solve
common problems - Can do them by hand
- Put might be tricky
- IDEs and separate tools support this
- IntelliJ, ReSharper, JRefactory, C Refactory
- Eclipse, JBuilder, next release of Visual Studio
46Why tools? Ponder these
- In Java, you decide to change a classs name.
What needs to changed? - In Java, you want to change the name of a method.
What needs to be changed? -
47Java Refactorings in Eclipse
- See help info in Eclipse. And Gallardo article.
- Renaming resources
- classes, methods, fields
- moving to another package
- inner and anonymous classes
- Class hierarchy modifications
- includes make an interface from a class
- Code level mods
- make a code segment a function
48Refactoring Processes
- Strategic
- general design
- Tactical
- detailed design
- Transformations
- mechanical steps
49Strategic and Tactical Refactoring Examples
- Strategic
- Convert Procedural Design to Objects
- Separate Domain From Presentation
- Tactical
- Tease Apart Inheritance
- Extract Hierarchy
50Strategic Refactoring Convert Procedural Design
to Objects
- Functional/Procedural Design
- data is passed from one function to the next,
which operates on it and returns data - Approach
- convert data entities into objects
- decompose behavior from functions
- move behavior to objects
51Introduce Architecture
- Identify general design metaphor (e.g. 3
tier/layer) - identify possible layers from procedural model
- Create interfaces
- Distribute objects/classes
- Edit and revise as necessary
52Strategic Refactoring Separate Domain From
Presentation
- Need to separate domain logic from presentation
logic (e.g. going from 2 to 3 tier system) - Starting point
- create domain objects for each GUI window
- move or duplicate domain oriented data in/to new
domain objects - move domain oriented logic to new domain objects
53Domain Presentation Separation and System
Architecture
- GUI will make calls to logic moved to domain
object - Domain interface needs to be determined
- calls may be made to facade for whole domain or
subset of domain objects that support domain
interfaces
54Tactical Refactoring Tease Apart Inheritance
- 2 or more inheritance hierarchies involved
- merged hierarchies have 2 different purposes
- approach
- identify major hierarchy
- extract subordinate hierarchy
- create instance variable in common superclass
- point to subordinate hierarchy
55Tactical Refactoring Extract Hierarchy
- Class grows in size and complexity as new
responsibilities are added - Starts to look like a representative of the Swiss
Army Knife Anti-Pattern - Cohesion is bad
- Usage create an instance and call one of its
methods
56Extract Hierarchy Steps
- Create subclasses of the super class for each of
the different kinds of behavior - Create a factory method for the superclass that
returns an instance of the appropriate subclass - Change calls to the superclass constructor to the
superclass factory method
57Extract Hierarchy and Factory Methods
- Standard use of factory methods
- factory method in a superclass is defined to
return an object defined by an interface - when superclass is subtyped, the factory method
is overriden to return a concrete class instance,
which implements the interface - Emphasis in this kind of use is on subclass
selection/definition at code writing time or
system start up (via properties object)
58Factory Methods and Extract Hierarchy Re-factoring
- Emphasis is on selection of subclass behavior at
run time - Techniques
- subtype argument code and switch statement used
to create appropriate subclass instance - subtype argument is name of class. Use
- (type) Class.forName(name).newInstance()
- to return instance of selected subtype
59Refactoring Steps - Categories
- Composing Methods
- Moving Features Between Objects
- Organizing Data
- Simplifying Conditional Expressions
- Making Method Calls Simpler
- Dealing with Generalization
60Composing Methods - Examples
- Method creation
- extract code and make a method from it
- issues local variables, parameter references
- if there are many local variables, turn the
method into an object whose class has a compute
method - class variables for each local variables
- now it will be easier to extract a new method
- Inlining code
- taking a small piece of code and removing its
method envelope - inlining an expression in place of the variable
whose value was derived from it
61Moving Features Between Objects - Examples
- Move Method
- Move Attribute
- Extract Class
- InLine Class
- Hide Class Delegate
- Remove Middleman
- Introduce Foreign Method
- Introduce Local Extension
62Refactoring Tools
- Simple edit-like tools
- e.g. changing method names
- find all possible references
- change names
- E.g. IntelliJ IDE