Josh Thompson     about     archive     turing     office hours

Refactoring practice: Getting rid of `attr_accessors` in `ogre.rb`

More refactoring practice!

I was reading Practical Object Oriented Design in Ruby, and then was looking at a Turing student’s work on the ogre mythical creature when I realized this was a perfect chance to explain a little about some principles of Object-Oriented Design.

Here’s a quick TWO MINUTE walk-through:

Here’s the code we’re starting with:

class Ogre
  attr_reader :name, :home, :swings, :encounter_counter
  def initialize(name, home = "Swamp")
    @name = name
    @home = home
    @swings = 0
    @encounter_counter = 0
  end
  
  def encounter(human)
    @encounter_counter += 1
    human.encounter_counter += 1
    if human.notices_ogre?
      swing_at(human)
    end
  end
  
  def swing_at(human)
    @swings += 1
  end
  
  def apologize(human)
    human.encounter_counter = 0
    @swings = 0
  end
end

class Human
  attr_accessor :encounter_counter
  attr_reader :name
  def initialize(name = "Jane")
    @name = name
    @encounter_counter = 0
  end
  
  def notices_ogre?
    return @encounter_counter % 3 == 0
  end
  
  def knocked_out?
    return true if encounter_counter >= 3
    false
  end
end

All tests pass.

I made changes to any time the Ogre class changed the “internal state” of another object (aka the Human class)

Copy-paste the code into your editor, and make the changes yourself!

The finished refactored version:

class Ogre
  attr_reader :name, :home, :swings, :encounter_counter
  def initialize(name, home = "Swamp")
    @name = name
    @home = home
    @swings = 0
    @encounter_counter = 0
  end
  
  def encounter(human)
    @encounter_counter += 1
    human.increment_encounters
    if human.notices_ogre?
      swing_at(human)
    end
  end
  
  def swing_at(human)
    @swings += 1
  end
  
  def apologize(human)
    human.reset_encounter_count
    @swings = 0
  end
end

class Human
  attr_reader :name, :encounter_counter
  def initialize(name = "Jane")
    @name = name
    @encounter_counter = 0
  end
  
  def increment_encounters
    @encounter_counter += 1
  end
  
  def reset_encounter_count
    @encounter_counter = 0
  end
  
  def notices_ogre?
    return @encounter_counter % 3 == 0
  end
  
  def knocked_out?
    return true if encounter_counter >= 3
    false
  end
end

Subscribe via Email

When I write a new post, you'll get an email the next Friday, straight to your inbox.