Having recently graduated from the Flatiron School, I am working my way through programming-related reading, beginning with Sandi Metz’s “Practical Object-Oriented Design in Ruby”. Click here for more posts about POODR.
The idea of inheritance may seem complicated but as with all complexity, there’s a simplifying abstraction. Inheritance is, at its core, a mechanism for automatic message delegation. It defines a forwarding path for not-understood messages. It creates relationships such that, if one object cannot respond to a received message, it delegates that message to another. You don’t have to write code to explicitly delegate the message, instead you define an inheritance relationship between two objects and the forwarding happens automatically.
That’s it, that’s all there is to inheritance. Not really. There’s a lot to inheritance, and it’s one of the things that makes Ruby the language that it is, in all its object-oriented glory.
In this post, I’ll speed through a couple key lessons about classical inheritance. Some of it was old news, some of it brand new.
Single vs. Multiple Inheritance
Multiple inheritance can get complicated (how do we know from which ancestor a descendant will inherit?). Metz explains that “Many object-oriented languages [including Ruby] sidestep these complications by providing single inheritance, whereby a subclass is allowed only one parent superclass. Ruby does this; it has single inheritance. A superclass may have many subclasses, but each subclass is permitted only one superclass” (112).
Inherent Inheritance (inheritance and
Whether you’ve implemented a class hierarchy or not, if you’ve used Ruby then you’ve used inheritance. An example:
nil?. Ruby contains two implementations of that method, one in
NilClass and the other in
nil? is called on an instance of
NilClass, it returns
true. On everything else, because everything elses is a subclass of
nil? message travels up the superclass hierarchy to
Object, which will then return
false. See the image below for a depiction of this.
Creating a Hierarchy has Costs (to duplicate or inherit?)
Any decision to implement inheritance should take into consideration the costs. Metz presents an example of a
MountainBike and a
RoadBike class, both of which could inherit from the
Bicycle superclass. But she asks, “Even though you now have a requirement for two kinds of bikes, this still may not be the right moment to commit to inheritance” and continues “A decision to proceed with the hierarchy accepts the risk that you may not yet have enough information to identify the correct abstraction. Your choice about whether to wait or to proceed hinges on how soon you expect a third bike to appear versus how much you expect the duplication to cost” (118-119).
That’s a technical term, I think. It’s a direct quote, anyway. Metz describes that, in implementing inheritance, it is important to push certain things down to the subclass, then pull them back up to the superclass to be inherited (and vice versa), even though it means moving code around multiple times. Her reasoning? Every programmer needs to ask themself: “What will happen when I’m wrong?”. Metz argues that “Every decision you make includes two costs: one to implement it and another to change it when you discover that you were wrong. Taking both costs into account when choosing among alternatives motivates you to make conservative choices that minimize the cost of change” (123).
super in any method passes that message up the superclass chain” (115). This is essential for inheritance. Adding
super to a method in a subclass will inherit the code of that same method of the subclass’ parent. If you’re confused, Stack Overflow’s got you covered.
I’ve copied Metz’s final example of inheritance using
Bicycle classes. As you can see, many of the methods (
defaults, etc.) are shared by the superclass, but some are unique to the subclasses.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
This is a finished example, but Metz likes to go through what she calls “antipatterns”—common patterns that appear beneficial but are actually detrimental—to demonstrate a concept, so make sure to read the full text.