refactoring

30
Refactoring 1

Upload: herez-moise-kattan

Post on 11-Apr-2017

401 views

Category:

Software


0 download

TRANSCRIPT

Page 1: Refactoring

Refactoring

1

Page 2: Refactoring

‣ 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

Page 3: Refactoring

‣ 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

Page 4: Refactoring

‣ 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

Page 5: Refactoring

‣ 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

Page 6: Refactoring

‣ 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

Page 7: Refactoring

‣ 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

Page 8: Refactoring

‣ 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

Page 9: 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

Page 10: Refactoring

<%= @invoice.customer.name %><%= @invoice.customer.address.street %><%= @invoice.customer.address.city %>,<%= @invoice.customer.address.state %><%= @invoice.customer.address.zip_code %>

10

AntiPattern: Voyeuristic Models

Page 11: Refactoring

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

Page 12: Refactoring

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

Page 13: Refactoring

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)

Page 14: Refactoring

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.

Page 15: Refactoring

‣ 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]

Page 16: Refactoring

‣ 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]

Page 17: Refactoring

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

Page 18: Refactoring

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

Page 19: Refactoring

‣ 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]

Page 20: Refactoring

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]

Page 21: Refactoring

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]

Page 22: Refactoring

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]

Page 23: Refactoring

‣ 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]

Page 24: Refactoring

‣ 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]

Page 25: Refactoring

‣ 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]

Page 26: Refactoring

‣ 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]

Page 27: Refactoring

‣ 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]

Page 28: Refactoring

‣ TDD refactoring

‣ Litter-pickup refactoring

‣ Comprehension refactoring

‣ Preparatory refactoring

‣ Planned refactoring

‣ Long-term refactoring

28

Workflows of refactoring

Page 29: 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

Page 30: Refactoring

‣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