Refactoring: Refactorings/Extract Surrounding Method
Jump to navigation
Jump to search
Two methods contain identical code, save for the middle of the methods. Ruby supports yielding to calling code blocks
Mechanics
- Use Extract Method on one piece of duplication. Name it after the duplicated behavior (this will become our surrounding method.
- Note: extracted surrounding method will still perform unique behavior for now
- Test
- Modify calling method to pass block parameter to surrounding method. Copy unique logic from surrounding method into the block.
- Replace unique logic in extracted method with yield keyword
- Identify any variables in the surrounding method that are needed by the unique logic and pass them as parameters in the call to yield
- Test
- Modify other methods that can now use the new surrounding method.
Example
Before
class Person
attr_reader :mother, :children, :name
def initialize(name, date_of_birth, date_of_death=nil, mother=nil)
@name, @mother = name, mother
@date_of_birth, @date_of_death = date_of_birth, date_of_death
@children = []
@children.add_child(self) if @mother
end
def add_child(child)
@children << child
end
def number_of_living_descendants
children.inject(0) do |count, child|
count += 1 if child.alive?
count + child.number_of_living_descendants
end
end
def number_of descendants_named(name)
children.inject(0) do |count, child|
count += 1 if child.name == name
count + child.number_of_descendants_named(name)
end
end
def alive?
@date_of_death.nil?
end
end
After
class Person
attr_reader :mother, :children, :name
def initialize(name, date_of_birth, date_of_death=nil, mother=nil)
@name, @mother = name, mother
@date_of_birth, @date_of_death = date_of_birth, date_of_death
@children = []
@children.add_child(self) if @mother
end
def add_child(child)
@children << child
end
def number_of_living_descendants
count_descendants_matching { |descendant| descendant.alive? }
end
def number_of descendants_named(name)
count_descendants_matching { |descendant| descendant.name == name }
end
def count_descendants_matching(&block)
children.inject(0) do |count, child|
count += 1 if yield child
count + child.count_descendants_matching(&block)
end
end
def alive?
@date_of_death.nil?
end
end