refactoring ruby code

Post on 24-May-2015

2.021 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

Refactoring Ruby code talk given @ Oxente Rails 2010

TRANSCRIPT

@caikehttp://caikesouza.com

Refatorando CódigoRuby

caike@envylabs.com

Métodos Ágeis

Testes

(Anti-)Patterns

Software Craftsmanship

"Any fool can write code that a computer can understand.

Good programmers write code that humans can understand"

(Martin Fowler)

Refatorar é ...

Limpar a casa sem mudar a fachada

Algumas razões

Design

Responder a mudanças

EntregasConstantes

Jan Feb Mar Apr May Jun Jul

Cost of Maintenance

Waterfall

Extreme Programming Explained: Embrace ChangeAddison Wesley, 2000

Jan Feb Mar Apr May Jun Jul

Cost of Maintenance Extreme Programming Explained: Embrace ChangeAddison Wesley, 2000

XP

Quando ?

novas features

bugs

Débito Técnico

Code Reviews

“The great thing about programming is pretty much everyone's code is shit.

Writing software is hard, so it's no problem finding dogs in someone's stuff”

(Zed Shaw)

exemplos.rb

managers = []

employees.each do |e| managers << e if e.is_manager?end

managers = []

employees.each do |e| managers << e if e.is_manager?end

Replace Loop withClosure Method

managers = []

employees.each do |e| managers << e if e.is_manager?end

managers = employees. select { |e| e.is_manager? }

class Movie def initialize(stars) @stars = stars end def recommended? ((@stars > 5) ? 8 : 1) >= 8 endend

class Movie def initialize(stars) @stars = stars end def recommended? ((@stars > 5) ? 8 : 1) >= 8 endend

Introduce ExplainingVariable

class Movie def initialize(stars) @stars = stars end def recommended? ((@stars > 5) ? 8 : 1) >= 8 endend

class Movie def initialize(stars) @stars = stars end def recommended? rating = (@stars > 5) ? 8 : 1 rating >= 8 endend

class Movie def initialize(stars) @stars = stars end def recommended? rating = (@stars > 5) ? 8 : 1 rating >= 8 endend

Replace TempWith Query

class Movie def initialize(stars) @stars = stars end def recommended? rating = (@stars > 5) ? 8 : 1 rating >= 8 endend

class Movie def initialize(stars) @stars = stars end def recommended? rating >= 8 end

def rating (@stars > 5) ? 8 : 1 end end

OMG OMG OMG OMG OMG

class Movie

def recommended? rating >= 8 end

def rating (@stars > 5) ? 8 : 1 end end

class Movie

def recommended? rating >= 8 end

def rating more_than_five_stars? ? 8 : 1 end

def more_than_five_stars? @stars > 5 end end

Inline Method

class Movie def initialize...end def recommended? rating >= 8 end

def rating more_than_five_stars? ? 8 : 1 end

def more_than_five_stars? @stars > 5 end end

class Movie def initialize...end def recommended? rating >= 8 end

def rating @stars > 5 ? 8 : 1 end

end

mock = mock('user')expectation = mock.expects(:find)expectation.with("1")expectation.returns([])

mock = mock('user')expectation = mock.expects(:find)expectation.with("1")expectation.returns([])

Replace TempWith Chain

mock = mock('user')expectation = mock.expects(:find)expectation.with("1")expectation.returns([])

mock = mock('user')mock.expects(:find).with("1"). returns([])

Commandvs.

Query

def expects ... selfend

def with ... selfend

def returns ... selfend

def charge(amount, card_number) begin conn = CC_Charger_Server.connect(...) conn.send(amount, card_number) rescue IOError => e Logger.log "Error: #{e}" return nil ensure conn.close endend

def charge(amount, card_number) begin conn = CC_Charger_Server.connect(...) conn.send(amount, card_number) rescue IOError => e Logger.log "Error: #{e}" return nil ensure conn.close endend

Extract SurroundingMethod

def charge(amount, ccnumber) begin conn = CC_Charger_Server.connect(...) conn.send(amount, card_number) rescue IOError => e Logger.log "Error: #{e}" return nil ensure conn.close endend

def charge(amount, card_number) connect do |conn| conn.send(amount, card_number) endend

def connect begin conn = CC_Charger_Server.connect(...) yield conn rescue IOError => e Logger.log "Error: #{e}" return nil ensure conn.close endend

def body_fat_percentage(name, age, height, weight, metric_system) ...end

body_fat_percentage("fred", 30, 1.82, 90, 1)body_fat_percentage("joe", 32, 6, 220, 2)

body_fat_percentage("fred", 30, 1.82, 90, 1)body_fat_percentage("joe", 32, 6, 220, 2)

Introduce Named Parameter

body_fat_percentage("fred", 30, 1.82, 90, 1)body_fat_percentage("joe", 32, 6, 220, 2)

body_fat_percentage("fred", :age => 30, :height => 1.82, :weight => 90, MetricSystem::METERS_KG)

body_fat_percentage("joe", :age => 32, :height => 6, :weight => 220, MetricSystem::FEET_LB)

def body_fat_percentage(name, age, height, weight, metric_system) ...end

def body_fat_percentage(name, params={}) # params[:age] # params[:height] # params[:weight] # params[:metric_system]end

user.posts.paginate(:page => params[:page], :per_page => params[:per_page] || 15)

user.posts.paginate(:page => params[:page], :per_page => params[:per_page] || 15)

Replace Magic Number with Symbolic Constant

user.posts.paginate(:page => params[:page], :per_page => params[:per_page] || 15)

CONTACTS_PER_PAGE = 15

user.posts.paginate(:page => params[:page], :per_page => params[:per_page] || CONTACTS_PER_PAGE)

class MountainBike def price ... endend

MountainBike.new(:type => :rigid, ...)MountainBike.new(:type => :front_suspension, ...)MountainBike.new(:type => :full_suspension, ...)

def price if @type_code == :rigid (1 + @comission) * @base_price end if @type_code == :font_suspension (1 + @comission) * @base_price + @front_suspension_price end if @type_code == :full_suspension (1 + @comission) * @base_price+ @front_suspension_price + @rear_suspension_price

end

end

def price if @type_code == :rigid (1 + @comission) * @base_price end

if @type_code == :font_suspension (1 + @comission) * @base_price + @front_suspension_priceend if @type_code == :full_suspension (1 + @comission) * @base_price+

@front_suspension_price + @rear_suspension_price

end if @type_code == :ultra_suspension ... end end

CLOSED for modificationOPEN for extension

Replace ConditionalWith Polymorphism

class MountainBike def price ... endend

module MountainBike def price ... endend

class RigidMountainBike include MountainBikeend

class FrontSuspensionMountainBike include MountainBikeend

class FullSuspensionMountainBike include MountainBikeend

RigidMountainBike.new(:type => :rigid, ...)

FrontSuspensionMountainBike.new(:type => :front_suspension, ...)

FullSuspensionMountainBike.new(:type => :full_suspension, ...)

class RigidMountainBike include MountainBike

def price (1 + @comission) * @base_price endend

class FrontSuspensionMountainBike include MountainBike def price (1 + @comission) * @base_price + @front_suspension_price end end

class FullSuspensionMountainBike include MountainBike def price (1 + @comission) * @base_price + @front_suspension_price + @rear_suspension_price endend

def price if @type_code == :rigid raise "should not be called" end if @type_code == :font_suspension (1 + @comission) * @base_price + @front_suspension_price end if @type_code == :full_suspension (1 + @comission) * @base_price+ @front_suspension_price + @rear_suspension_price

end end

def price if @type_code == :rigid raise "should not be called" end if @type_code == :font_suspension raise "should not be called" end if @type_code == :full_suspension raise "should not be called" end end

def price if @type_code == :rigid raise "should not be called" end if @type_code == :font_suspension raise "should not be called" end if @type_code == :full_suspension raise "should not be called" end end

class RigidMountainBike include MountainBikeend

class FrontSuspensionMountainBike include MountainBikeend

class FullSuspensionMountainBike include MountainBikeend

top related