Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save jayzz55/8620fc8af47564773754 to your computer and use it in GitHub Desktop.

Select an option

Save jayzz55/8620fc8af47564773754 to your computer and use it in GitHub Desktop.

2: Designing classes with a single responsibility

Depend on Behaviour not Data

Hide Data Structures

Bad Examples:

class ObscuringReferences attr_reader :data
  def initialize(data)
  @data = data end
  def diameters
    # 0 is rim, 1 is tire 
    data.collect {|cell| cell[0] + (cell[1] * 2)} 
  end
  # ... many other methods that index into the array
end

# rim and tire sizes (now in millimeters!) in a 2d array 
@data = [[622, 20], [622, 23], [559, 30], [559, 40]]

Good Examples:

class RevealingReferences 
  attr_reader :wheels 
  def initialize(data)
    @wheels = wheelify(data) 
  end
  def diameters 
    wheels.collect {|wheel| wheel.rim + (wheel.tire * 2)} 
  end
  # ... now everyone can send rim/tire to wheel
  
  Wheel = Struct.new(:rim, :tire) 
  def wheelify(data)
    data.collect {|cell| Wheel.new(cell[0], cell[1])}
  end 
end

The diameters method above now has no knowledge of the internal structure of the array. All diameters knows is that the message wheels returns an enumerable and that each enumerated thing responds to rim and tire. What were once references to cell[1] have been transformed into message sends to wheel.tire.

Single Responsibility

class Gear
  attr_reader :chainring, :cog, :wheel
  def initialize(chainring, cog, wheel=nil)
    @chainring = chainring
    @cog       = cog
    @wheel     = wheel
  end
  def ratio
    chainring / cog.to_f
  end
  def gear_inches
    ratio * wheel.diameter
  end 
end

class Wheel
  attr_reader :rim, :tire
  def initialize(rim, tire) 
    @rim = rim
    @tire = tire
  end
  def diameter
    rim + (tire * 2)
  end
  def circumference 
    diameter * Math::PI
  end 
end

@wheel = Wheel.new(26, 1.5) puts @wheel.circumference 
# -> 91.106186954104
puts Gear.new(52, 11, @wheel).gear_inches 
# -> 137.090909090909
puts Gear.new(52, 11).ratio 
# -> 4.72727272727273

The path to changeable and maintainable object-oriented software begins with classes that have a single responsibility. Classes that do one thing isolate that thing from the rest of your application. This isolation allows change without consequence and reuse without duplication.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment