The document discusses refactoring, which is defined as changing the internal structure of code without changing its external behavior or functionality. The purpose of refactoring is to improve aspects like design, structure and maintainability without introducing bugs. Common reasons to refactor include improving code quality, reducing complexity, and increasing understandability and flexibility. The document also discusses code smells that indicate when refactoring may be needed and provides examples of refactoring techniques.
2. ‣ Refactoring is the process of changing a software
system in such a way that it does not* alter the
external behavior of the code yet improves its
internal structure [Fowler et al.; 2002]
‣ The art of safely* improving the design of existing
code [Fowler et al.; 2009]
*Importance of Testing in Refactoring 2
Definition
3. ‣ Refactoring (noun): A change made to the
internal structure of software to make it easier to
understand and cheaper to modify without
changing its observable behavior
‣ Refactor (verb): To restructure software by
applying a series of refactorings without changing
its observable behavior 3
Definition
4. ‣ Refactoring does not include any functional
change to the system
‣ Refactoring is not “Rewriting from scratch”
‣ Refactoring is not just any restructuring
intended to improve the code
4
Refactoring is not
5. ‣ Start with an existing code and make it better
‣ Change the internal structure while preserving
the overall semantics
‣ Refactoring changes the programs in small steps
If you make a mistake, it is easy to find the bug
5
Refactoring is
6. ‣ Improves Quality of the Code
‣ Improves Maintainability while reducing Coupling
‣ Improves the Design of Software
‣ Makes Software Easier to Understand
‣ Helps You Find Bugs
6
Benefits
7. ‣ Refactor When You Add Function
‣ Refactor When You Need to Fix a Bug
‣ Refactor As You Do a Code Review
‣ Refactoring for Greater Understanding
The third time you do something similar, you refactor
7
When
8. ‣ Changing Interfaces
Don’t publish interfaces prematurely. Modify your
code ownership policies to smooth refactoring
‣ Databases
‣ Design Changes That Are Difficult to Refactor
‣ When Shouldn’t You Refactor?
‣ It Takes A While to Create Nothing
‣ Refactoring and Performance 8
Problems with refactoring
9. [Pytel et al.; 2010]
class Address < ActiveRecord::Base
belongs_to :customer
end
class Customer < ActiveRecord::Base
has_one :address
has_many :invoices
end
class Invoice < ActiveRecord::Base
belongs_to :customer
end 9
AntiPattern: Voyeuristic Models
11. class Customer < ActiveRecord::Base
has_one :address
has_many :invoices
def street
Address.street
end
def city
Address.city
end
def state
Address.state
end
def zip_code
Address.zip_code
end
end
Principle of Least Knowledge
11
12. class Invoice < ActiveRecord::Base
belongs_to :customer
def customer_name
Customer.name
end
def customer_street
Customer.street
end
def customer_city
Customer.city
end
def customer_state
customer.state
end
def customer_zip_code
customer.zip_code
end
Principle of Least Knowledge
12
13. class Address < ActiveRecord::Base
belongs_to :customer
end
<%= @invoice.customer_name %>
<%= @invoice.customer_street %>
<%= @invoice.customer_city %>
<%= @invoice.customer_state %>
<%= @invoice.customer_zip_code %>
“use only one dot”
13
Principle of Least Knowledge (Demeter)
14. class Customer < ActiveRecord::Base
has_one :address
has_many :invoices
delegate :street, :city, :state, :zip_code, :to => :address
end
class Invoice < ActiveRecord::Base
belongs_to :customer
delegate :name,
:street,
:city,
:state,
:zip_code,
:to => :customer,
:prefix => true
end
14
Thanks to the class-level delegate method!
You have the benefit of following the Law of
Demeter without so much extra clutter in
your models.
15. ‣ Case Statements
‣ Comments
‣ Long Method
‣ Long Parameter List
‣ Middle Man
‣ Repetitive Boilerplate
‣ Data Clumps
…
Chapter 3 - Bad Smells in Code [Fowler et al.; 2009] 15
If it stinks, change it [Grandma Beck; 2002]
16. ‣ A class is doing too much simple delegation
One of the prime features of objects is encapsulation,
hiding internal details from the rest of the world.
Encapsulation often comes with delegation.
You ask a director whether is free for a meeting. The Director
delegates the message to a diary and gives you an answer.
There is no need to know whether the director uses a diary, an
electronic gizmo, or a secretary.
16
Remove middle man [Fowler et al.; 2002]
17. Get the client to call the delegate directly
‣ If half the methods are delegating to this other class. It is time
to use remove middle man and talk to the object that really
knows what’s going on
Inline Method:
If only a few methods
aren’t doing much, use
Inline Method to inline
them into the caller.
17
Remove middle man
18. class Person…
def initialize(department)
@department = department
end
def manager
@department.manager
end
class Department
attr_reader :manager
def initialize(manager)
@manager = manager
end
...
This is simple to use and encapsulates the
department. However, if a lot of methods are
doing this, I end up with too many of these
simple delegations on the person. That’s
when it is good to remove the middle man.
manager = john.manager
Take each method at a time. Find clients
that use the method on person and change it
to first get the delegate. Then use it:
Make an accessor for the delegate:
class Person…
attr_reader :department
manager = john.department.manager 18
Remove middle man
19. ‣ A method’s body is just as clear as its name
Put the method’s body into the body of its callers and remove
the method: def get_rating
more_than_five_late_deliveries ? 2 : 1
end
def more_than_five_late_deliveries
@number_of_late_deliveries > 5
end
def get_rating
@number_of_late_deliveries > 5 ? 2 : 1
end
19
Inline method [Fowler et al.; 2002]
20. When you feel the need to write a comment, first try to refactor
the code so that any comment becomes superfluous.
20
Comments Should Describe Things
That Aren’t Obvious From The Code:
Why, not What
[9.4 Book SaaS apud John Ousterhout]
Comments are a sweet smell [Fowler]
21. 21
# Scan the array to see if the symbol exists
# Loop through every array index, get the third value of the list
# in the content to determine if it has the symbol we are
# looking for. Set the result to the symbol if we find it.
Comments [Fox, A.; Patterson, D.; 2015]
22. Symptoms that often indicate code smells:
‣ Is it SHORT?
‣ Does it do ONE thing?
‣ Does it have FEW arguments?
‣ Is it a consistent level of ABSTRACTION?
22
SOFA [Martin, R.; 2008]
23. ‣ One of the easiest ways to remove duplication is
Extract Method.
‣ Extract the method and call it from multiple places.
‣ Some kinds of methods become so commonplace that
we can go even further.
‣ Take for example attr_reader in Ruby.
23
Repetitive boilerplate [Fowler et al.; 2009]
24. ‣ Ninety-nine percent of the time, all you have to do to
shorten a method is Extract Method
‣ Find parts of the method that seem to go nicely
together and make a new method
24
Long method [Fowler et al.; 2002]
25. ‣ You have a code fragment that can be grouped together
Turn the fragment into a method whose name explains the
purpose of the method
def print_owning(amount)
Print_banner
puts "name: #{@name}"
puts "amount: #{amount}"
end
def print_owning(amount)
print_banner
print_details amount
end
def print_details(amount)
puts "name: #{@name}"
puts "amount: #{amount}"
end 25
Extract method [Fowler et al.; 2002]
26. ‣ You have different methods that use the same group of
variables
Treatment: Introduce a Parameter Object
def directions(lat, long)
...
end
def distance(lat, long)
...
end
def directions(location)
...
end
def distance(location)
...
end
26
Data clumps [Fowler et al.; 2002]
27. ‣ The code has a temporary variable that’s being used to hold
intermediate results of an expression
Return self on the methods so it’s possible to chain the calls
mock = Mock.new
expectation = mock.expects(:
a_method)
expectation.with("arguments")
expectation.returns([1, :array])
mock = Mock.new
mock.expects(:a_method_name).with
("arguments").returns([1, :array])
27
Replace temp with chain [Fowler; 2002]