I've been reading Sandi Metz's book on OOP in Ruby. In the chapter on inheritance there is a section on the template method pattern.
It talks about having a base class with common behaviour, then subclasses that inherit this behaviour and implement their own specialised interfaces as needed.
The book goes on to discuss how to send messages from the base class to the subclass methods to get the specific behaviour and override defaults, like so:
class Bicycle
attr_reader :size, :chain, :tire_size
def initialize(**opts)
@size = opts[:size]
@chain = opts[:chain] || default_chain
@tire_size = opts[:tire_size] || default_tire_size
end
def default_chain
"11-speed"
end
def default_tire_size
raise NotImplementedError
end
end
class RoadBike < Bicycle
def default_tire_size
"23"
end
end
What is the benefit of getting the base class to make this call to a method in a sub class, if the method can be defined in the subclass itself?
CodePudding user response:
To see why is this desirable we need to look at the previous iteration of the code (on p126 in my copy):
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
end
def default_chain
'10-speed'
end
end
Spot the problem? There is an implicit and undocumented expectation that subclasses will implement default_tire_size
. If they don't the code blows up in the constructor.
Sandi says on p128:
Explicitly stating that subclasses are required to implement a message provides useful documentation for those who can be relied upon to read it and useful error messages for those who cannot.
The superclass Bicycle
is an abstraction of the behavior common to all concrete subclasses. In this example the default_tire_size
in Bicycle
's public interface tells interested parties (e.g. maintainers of Bicycle
subclasses) that there is an expectation that a default tire size will be set. An exception is raised if the method is not implemented in a subclass. The exception is a clear pointer of what has gone wrong - you ain't implemented something you oughta. Much better than a potentially puzzling undefined local variable or method 'default_tire_size'
no?