Base Class Hubs
When designing base classes, there are a number of guidelines and patterns that can impact the design. One of those is the idea of “hubs”. Thanks to my wife for the inspiration on the name.
There are several opposing forces at play when creating the base class. We often want to provide a rich API to clients of the class hierarchy, but we also want to be able to easily create subclasses without having to override a bunch of methods. How do we balance these forces?
One way is to create a small set of hub methods that everything else flows through. All of the rich API methods are implemented in terms of the hub methods. Subclasses only need to be concerned with implementing or customizing these hub methods.
Smalltalk and Ruby both use this pattern for collection enumeration.
Smalltalk’s enumeration methods (#collect:
, #select:
, #reject:
,
#detect:
, etc.) are all implemented in terms of #do:
. Ruby’s
Enumerable
mixin is similar, in that everything is implemented in
terms of :each
. #do:
and :each
are hub methods.
Ruby also uses this pattern in the Comparable
mixin, where
all of the methods are implemented in terms of the spaceship operator,
<=>
. Again, <=>
is a hub method.
Even Rails uses this pattern. One instance of it is in
ActiveRecord::Persistence
. Both :save
and :save!
are
implemented in terms of :create_or_update
. So if you wanted to use
standard Ruby inheritance instead of ActiveRecord callbacks, you can
override :create_or_update
and your changes would affect both
:save
and :save!
. Thanks to a helpful thread on the
Ruby Rogues Parley forum for this
last example.
Next time you’re working on a base class, think about using hub methods as a way to balance the forces of easier subclassing and rich client APIs.