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
endAfter
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