Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save jayzz55/a4ce43280402807f161c to your computer and use it in GitHub Desktop.
Save jayzz55/a4ce43280402807f161c to your computer and use it in GitHub Desktop.

6: Acquiring Behavior Through Inheritance

Inheritance is, at it's core, a mechanism for automatic message delegation. It defines a forward path for not-understood messages.

Subclasses are specializations of their superclasses

Template Method Pattern

Any class that implements the template method pattern must supply an implementation for every message it sends, even if all it does is raise a NotImplementedError.

One way around the super problem is for the superclass to send hook messages at appropriate integration points. If a subclass needs to add or modify the behavior of the superclass, it can implement an appropriate hook method. As noted above, the superclass must always have an implementation any shared methods; though usually the superclass’s hook method is just a no-op.

class Bicycle
  attr_reader :size, :chain, :tire_size

  def initialize(args={})
    @size       = args[:size]
    @chain      = args[:chain]     || default_chain
    @tire_size  = args[:tire_size] || default_tire_size
    post_initialize(args)
  end

  def spares
    { tire_size: tire_size,
      chain:     chain}.merge(local_spares)
  end

  # It is important that the parent class also have a default_tire_size method - even if all it does is raise a NotImplementedError.
  def default_tire_size
    raise NotImplementedError
  end

  # subclasses may override
  # This is the Hook Message instead of using a super
  def post_initialize(args)
    nil
  end

  # This is the Hook Message instead of using a super
  def local_spares
    {}
  end

  def default_chain
    '10-speed'
  end

end

Then the subclass will be created with the following:

class RoadBike < Bicycle
  attr_reader :tape_color

  def post_initialize(args)
    @tape_color = args[:tape_color]
  end

  def local_spares
    {tape_color: tape_color}
  end

  def default_tire_size
    '23'
  end
end

class MountainBike < Bicycle
  attr_reader :front_shock, :rear_shock

  def post_initialize(args)
    @front_shock = args[:front_shock]
    @rear_shock =  args[:rear_shock]
  end

  def local_spares
    {rear_shock:  rear_shock}
  end

  def default_tire_size
    '2.1'
  end
end

and adding a new variants will be much easier like this:

class RecumbentBike < Bicycle
  attr_reader :flag

  def post_initialize(args)
    @flag = args[:flag]
  end

  def local_spares
    {flag: flag}
  end

  def default_chain
    '9-speed'
  end

  def default_tire_size
    '28'
  end
end

bent = RecumbentBike.new(flag: 'tall and orange')
bent.spares
# -> {:tire_size => "28",
#     :chain     => "10-speed",
#     :flag      => "tall and orange"}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment