Complete Bicycle Example: Classical Inheritance

This example showcases a complete implementation of a Bicycle system using classical inheritance. It helps us understand the limitations of this approach, setting the stage for the transition to composition.

Note: This example is inspired by Sandi Metz's work in Practical Object-Oriented Design in Ruby (POODR).

Classical Inheritance Implementation

class Bicycle
  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

  def default_tire_size
    raise NotImplementedError, "This #{self.class} cannot respond to: default_tire_size"
  end

  def default_chain
    "10-speed"
  end

  def post_initialize(args)
    nil
  end

  def local_spares
    {}
  end
end

class RoadBike < Bicycle
  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
  def post_initialize(args)
    @front_shock = args[:front_shock]
    @rear_shock = args[:rear_shock]
  end

  def local_spares
    { front_shock: @front_shock }
  end

  def default_tire_size
    "2.1"
  end
end

class RecumbentBike < Bicycle
  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

road_bike = RoadBike.new(size: "M", tape_color: "red")
mountain_bike = MountainBike.new(size: "L", front_shock: "Manitou", rear_shock: "Fox")
recumbent_bike = RecumbentBike.new(size: "S", flag: "Tall")

puts road_bike.spares
puts mountain_bike.spares
puts recumbent_bike.spares
        

This implementation demonstrates the rigid structure of inheritance. While functional, it often leads to duplication, tight coupling, and difficulty in extending behavior. In the next section, we’ll explore the challenges that arise from this approach.

Next: Challenges with Inheritance