Inheritance-and-modules
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 redunancy 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:
class Plant
end
class Rose <Plant
end
class Algae < Plant
end
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:
class Plant
def live
puts "multicellular, can reproduce, absorbs light, circulate or absorb water"
end
end
class Rose <Plant
def make_flower
puts "leaf, stem, roots"
end
end
class Moss < Plant
def produce_spore
puts "simple, can live on another plant"
end
end
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:
irb> Math.sqrt(2)
=>1.41421
irb> Math::PI
=>3.14159
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:
irb> include Math
=>Object
irb> sqrt(2)
=>1.41421
irb> PI
=>3.14159
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:
module Plant
def live
puts "multicellular, can reproduce, absorbs light, circulate or absorb water"
end
end
class Rose <Plant
include Plant
def make_flower
puts "leaf, stem, roots"
end
end
class Moss < Plant
include Plant
def produce_spore
puts "simple, can live on another plant"
end
end
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:
module Memorable
module InstanceMethods
def initialize
self.class.all << self
end
end
module ClassMethods
def self.extended(base)
base.reset_all
end
def reset_all
@all = [] #still do not quite understand why we don't use @@all here
end
def count
self.all.size
end
def all
@all
end
end
end
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.
class Artist
attr_accessor :name
attr_reader :songs
extend Memorable::ClassMethods
include Memorable::InstanceMethods #modules
extend Findable
def initialize
super
@songs = []
end
[code omitted]
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: 1. http://www.ruby-doc.org/core-1.9.3/Module.html 2. http://www.rubyist.net/~slagell/ruby/modules.html 3. http://ducktypo.blogspot.com/2010/08/why-inheritance-sucks.html 4. http://ruby.about.com/od/beginningruby/a/mixin.htm