refactoring
TRANSCRIPT
Refactoring
1
‣ 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 Refactoring2
Definition
‣ 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
‣ 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
‣ 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
‣ Improves Quality of the Code
‣ Improves Maintainability while reducing Coupling
‣ Improves the Design of Software
‣ Makes Software Easier to Understand
‣ Helps You Find Bugs6
Benefits
‣ 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
‣ Changing InterfacesDon’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
[Pytel et al.; 2010]
class Address < ActiveRecord::Basebelongs_to :customer
endclass Customer < ActiveRecord::Base
has_one :addresshas_many :invoices
endclass Invoice < ActiveRecord::Base
belongs_to :customerend
9
AntiPattern: Voyeuristic Models
<%= @invoice.customer.name %><%= @invoice.customer.address.street %><%= @invoice.customer.address.city %>,<%= @invoice.customer.address.state %><%= @invoice.customer.address.zip_code %>
10
AntiPattern: Voyeuristic Models
class Customer < ActiveRecord::Basehas_one :addresshas_many :invoicesdef street
Address.streetenddef city
Address.cityenddef state
Address.stateenddef zip_code
Address.zip_codeend
end
Principle of Least Knowledge
11
class Invoice < ActiveRecord::Basebelongs_to :customerdef customer_name
Customer.nameenddef customer_street
Customer.streetenddef customer_city
Customer.cityenddef customer_state
customer.stateenddef customer_zip_code
customer.zip_codeend
end
Principle of Least Knowledge
12
class Address < ActiveRecord::Basebelongs_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)
class Customer < ActiveRecord::Basehas_one :addresshas_many :invoicesdelegate :street, :city, :state, :zip_code, :to => :address
endclass Invoice < ActiveRecord::Base
belongs_to :customerdelegate :name,:street,:city,:state,:zip_code,:to => :customer,:prefix => true
end14
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.
‣ 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]
‣ 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]
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
class Person…def initialize(department)
@department = departmentenddef manager
@department.managerend
class Departmentattr_reader :managerdef initialize(manager)
@manager = managerend
...
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
‣ A method’s body is just as clear as its namePut the method’s body into the body of its callers and remove the method: def get_rating
more_than_five_late_deliveries ? 2 : 1enddef more_than_five_late_deliveries
@number_of_late_deliveries > 5end
def get_rating@number_of_late_deliveries > 5 ? 2 : 1
end19
Inline method [Fowler et al.; 2002]
When you feel the need to write a comment, first try to refactorthe code so that any comment becomes superfluous.
20
Comments Should Describe ThingsThat Aren’t Obvious From The Code:
Why, not What[9.4 Book SaaS apud John Ousterhout]
Comments are a sweet smell [Fowler]
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]
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]
‣ 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]
‣ 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]
‣ 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_bannerputs "name: #{@name}"puts "amount: #{amount}"
end
def print_owning(amount)print_bannerprint_details amount
enddef print_details(amount)
puts "name: #{@name}"puts "amount: #{amount}"
end25
Extract method [Fowler et al.; 2002]
‣ You have different methods that use the same group of variablesTreatment: Introduce a Parameter Object
def directions(lat, long)...
enddef distance(lat, long)
...end
def directions(location)...
enddef distance(location)
...end
26
Data clumps [Fowler et al.; 2002]
‣ The code has a temporary variable that’s being used to hold intermediate results of an expressionReturn self on the methods so it’s possible to chain the calls
mock = Mock.newexpectation = mock.expects(:a_method)expectation.with("arguments")expectation.returns([1, :array])
mock = Mock.newmock.expects(:a_method_name).with("arguments").returns([1, :array])
27
Replace temp with chain [Fowler; 2002]
‣ TDD refactoring
‣ Litter-pickup refactoring
‣ Comprehension refactoring
‣ Preparatory refactoring
‣ Planned refactoring
‣ Long-term refactoring
28
Workflows of refactoring
‣ Refactoring plugin (Eclipse)
‣ Aptana Studio 3
‣ RubyMine
‣ Rails tool reek finds code smells
‣ metric_fu (book SaaS: saikuro (CC), flog (ABC))
‣ Rake metrics
‣ CodeClimate
‣ Mezuro 29
Tools
‣M. Fowler; K. Beck; Addison Wesley, 2002;Refactoring: Improving the Design of Existing Code‣Fields, J.; S. Harvie; M. Fowler; K. Beck; Addison Wesley, 2009; Refactoring Ruby Edition‣Pytel, C.; Saleh, T.; Addison Wesley, 2010; Rails Antipatterns Best Practice Ruby on Rails Refactoring‣Martin, R.; Prentice Hall, 2008; Clean Code: A Handbook of Agile Software Craftsmanship‣Fox, A.; Patterson, D.; Strawberry Canyon, 2015; Construindo Software como Serviço (Capítulo 9)‣http://refactoring.com/catalog/
30
References