The following is a guest post by Margaret Lee and originally appeared on her blog. Margaret is currently in the Ruby-003 class at The Flatiron School. You can follow her on Twitter here.
At the end of last week, we discussed inheritance and modules. I needed to review it as not all of it had sunk-in at runtime-lecture. So here goes:
If one of the many purposes of Ruby Classes is to help free us from redundancy by making certain methods available to all instances of that class. Along that line it seems inheritance builds on the notion of “sharing” in that it allows a Class to make its capabilities available to sub-classes and include further specificity and/or additional methods where necessary.
As I researched inheritance up, people liked using taxonomies to help illustrate this. I’ll do the same but instead use plants instead of animals/mammals/etc. The plant kingdom has certain characteristics that hold true for all things defined as plants including such attributes as— they are cellular, they can convert energy from sunlight to food, they can propogate etc.
There are two primary subclasses of plants: seed-bearing and spore-bearing. Both subclasses possess the primary plant kingdom characteristics (cellular, absorb sunlight) but they also possess additional specifics such as being able to have flowers, being able to either absorb water or circulate fluids throughout itself or being of a certain sex or asexual.
So in Ruby:
The Rose class and Algae class are “Child” classes that inherit (denoted by <
) all characteristics of the “Parent” class Plant. This makes it easy to ascribe further specificity:
A Rose and Moss are a type of plant but also have their own specialized things they can do. Its important to note that in Ruby, at least, a Class can only inherit features from one other class.
This brings us to Modules and Mixins. Modules are, according to Ruby docs, a collection of methods and constants. Hmmm. Need more help here. As it turns out, Ruby itself possesses a number of modules in its standard library. A quick example in IRB:
The ::
operator denotes which module Ruby should use for the value of a constant. If we want to refer to the methods or constants of a module directly without using ::
, we can include
that module:
As mentioned in the Pickaxe, “Modules define a namespace, a sandbox in which your methods and constants can play without having to worry about being stepped on by other methods and constants.” This is one important use of modules.
The other key important use of modules is that, like inheritance, modules allow functionality to be “shared” between classes thereby streamlining the number of times you have to write the same methods. As one person describes,
“mixins are a group of methods not yet attached to any class. They exist in a module and are not useful until included in a class. Once included they are now normal methods of a class.” (source)
So illustrating on the above Class inheritance structure:
Methods within a module can either be instance methods or module level methods. In the example above, the module is included within a class, thus the module’s methods are “mixed in” as instance methods and apply to all instances of the Rose and Moss classes. As
Why may modules be a better choice over classes with inheritance?
As this person states: Modules are generally more flexible than superclasses. Modules can be managed at runtime, because include
is just regular method call, while superclasses are set in stone as you write your class definitions. Modules are much easier to use and test in isolation than tightly coupled hierachy of classes. You can include as many modules as you like, while you can only have one superclass per class.
Modules can also be applied at the Class level by “extending” within the Class. This would allows the methods within the module to be accessed by other classes. This piece of Avi code from Friday’s Playlister was what had led me to look into inheritance, modules and mixins a little further to begin with:
memorable.rb
We see in the partial Artist.rb file below, all the methods in module Findable and ClassMethods specifically in Memorable will be Artist Class methods. I also noted the use of super
, whereby Ruby searches upward in the ancestry to find the method initialize
so that it doesn’t need to be written here again.
artist.rb
So the primary difference in usage as I try to apply this in my own code. include
applies to Class Instance Methods and extend
when the module is to be extended to other classes instead of being included. Access to the module method is done through the class instead of the instance.
This serves as a start in learning about something unique and powerful to Ruby. From the readings it seems that other languages handle inheritance very differently (multiple inheritances etc.,). Furthermore, really cool things happen when we start looking at how, for instance, mixins from Ruby’s standard library (eg, Enumerable, Comparable, or Math) can work with code in the class using it. Looking forward to putting some of this into greater practice going forward.
Resources and other interesting posts on inheritance and modules: