sivanes
sivanes

Reputation: 733

making an example of inheritance, well... bad

I'm reading a book Object Oriented Design in Ruby and there is a part that shows MountainBike class as an example of bad inheritance practices. It is not shown exactly, but implied that the Bicycle class that the former inherits from is written the same way. So, the code we have is the following...

class Bicycle
  attr_reader :style, :size, :tape_color,
              :front_shock, :rear_shock

  def initialize(args)
    @style = args[:style]
    @size = args[:size]
    @tape_color = args[:tape_color]
    @front_shock = args[:front_shock]
    @rear_shock = args[:rear_shock]
  end

  def spares
    if style == :road
      { chain: '10-speed',
        tire_size: '23',
        tape_color: tape_color }
    else
      { chain: '10-speed',
        tire_size: '2.1',
        rear_shock: rear_shock }
    end
  end

end

class MountainBike < Bicycle
  attr_reader :front_shock, :rear_shock

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

  def spares
    super.merge(rear_shock: rear_shock)
  end
end

mountain_bike = MountainBike.new(
        size:        'S',
        front_shock: 'Manitou',
        rear_shock:  'Fox' )

puts mountain_bike.size
puts mountain_bike.spares

The output for mountain_bike.spares is supposed to be this scramble

{:tire_size => "23", :chain => "10-speed", :tape_color => nil, :front_shock => 'Manitou', :rear_shock => "Fox"}

But, what I get is

{:chain=>"10-speed", :tire_size=>"2.1", :rear_shock=>"Fox"}

which obviously doesn't prove the intended point. How can class Bicycle be re-written to match the "wrong" output?

Upvotes: 0

Views: 99

Answers (2)

Sandi Metz
Sandi Metz

Reputation: 16

Sivanes, you're got the wrong Bicycle class.

In Practical-Object Oriented Design in Ruby, on page 115, when the MountainBike class above is introduced, it says:

The following is a first attempt at a MountainBike subclass. This new subclass is a direct descendent of the original Bicycle class.

The original Bicycle class is:

class Bicycle
  attr_reader :size, :tape_color

  def initialize(args)
    @size       = args[:size]
    @tape_color = args[:tape_color]
  end

  # every bike has the same defaults for
  # tire and chain size
  def spares
    { chain:        '10-speed',
      tire_size:    '23',
      tape_color:   tape_color}
  end

  # Many other methods...
end

Running the code with this version of Bicycle will give you the correct result.

Upvotes: 0

Surya
Surya

Reputation: 16002

I don't see anything wrong here.

Look at the code in Bicycle for method spares:

def spares
  if style == :road
    { chain: '10-speed',
      tire_size: '23',
      tape_color: tape_color }
  else
    { chain: '10-speed',
      tire_size: '2.1',
      rear_shock: rear_shock }
  end
end

Do you see an if condition: if style == :road? Right? Good, please make a note here.

Now, look at the method spares inside class MountainBike which is inherited from Bicycle:

def spares
  super.merge(rear_shock: rear_shock)
end

Do you see super? It will call parent's class method, in this case spares method. Since you are initializing the object of MountainBike class with this:

mountain_bike = MountainBike.new(size:        'S',
                                 front_shock: 'Manitou',
                                 rear_shock:  'Fox' ) # no style in argument!!

It is quite apparent that the condition we just noted above will fall in else, which will give:

{ chain: '10-speed',
  tire_size: '2.1',
  rear_shock: rear_shock }

and then merge:

{ chain: '10-speed',
  tire_size: '2.1',
  rear_shock: rear_shock }.merge(rear_shock: rear_shock)

and since rear_shock's value is: 'Fox', the above code will be finally evaluated to:

{ chain: '10-speed', tire_size: '2.1', rear_shock: 'Fox' }

Isn't that what exactly you were expecting?

Note that {a: 'b'} is as same as writing {:a => 'b'}, it's just a syntax sugar.

So, my point is either the book you've read has some different code/approach to show the inheritance example or it has a miss-printed output. There's no way it can print:

{:tire_size => "23", :chain => "10-speed", :tape_color => nil, :front_shock => 'Manitou', :rear_shock => "Fox"}

Upvotes: 2

Related Questions