Reputation: 733
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
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
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