Today’s post is an excerpt from a tutorial I am creating about Rails API development that I hope to have available soon. I hope you enjoy it and find it useful!
Many times you might have heard about programming languages supporting multiple inheritance… but is Ruby one of them? Before I answer that question let us see how inheritance and modules in Ruby look like.
Inheritance
Inheritance is a mechanism that allows a class to inherit the attributes and behavior of another class. The class that is inheriting is usually called the subclass. or child class, and the class it inherits from is called the superclass, or parent class.
In Ruby, inheritance is represented with the syntax Subclass < Superclass
. If you have B < A
, you can read that in the following ways: class B
is a child class of A
, B
inherits from A
or A
is the parent of B
.
Let’s see it with an example. Imagine you need to model the medical staff in a hospital. You have doctors that cannot do surgery, and surgeons that are a special kind of doctors that can do surgery. One possible way to model this in Ruby would be the following.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class Doctor attr_reader :name, :specialty def initialize(name, specialty) @name = name @specialty = specialty end def does_surgery? false end end class Surgeon < Doctor def does_surgery? true end end |
As you can see, there is a parent class Doctor
, which defines two attributes (name
and specialty
) and a method, Doctor#does_surgery?
, which returns false
indicating that a regular doctor is not qualified to do surgery. It also defines two attribute reader methods so that consumers of this class are able to fetch name
and specialty
values, but not modify them. On the other hand, there is a child class Surgeon
, that inherits everything from its parent class Doctor
. That is, the name
and specialty
attributes, as well as their corresponding accessor methods, but overrides the method does_surgery?
returning true
to adapt the class to surgeons’ capabilities.
Let’s define an object of class Doctor
and check its properties.
1 2 3 4 5 6 7 8 |
irb(main):019:0> doctor = Doctor.new("Sienna Johnson", "Allergist") irb(main):020:0> doctor.name => "Sienna Johnson" irb(main):021:0> doctor.specialty => "Allergist" irb(main):022:0> doctor.does_surgery? => false |
Now it is your turn to define a Surgeon
object and see if there is any difference with the behavior of the Doctor
object. Create a new Surgeon
object as it follows.
1 |
surgeon = Surgeon.new("Gene Hampton", "Dermatologist") |
As you can see, the Surgeon
constructor can also take the name and specialty attributes, although the Surgeon class has not explicitly defined that. This is because the Surgeon
class inherits its constructor from its parent Doctor
class.
Let’s now check if there are more differences or similarities.
1 2 3 4 5 6 7 |
irb(main):025:0> surgeon.name => "Gene Hampton" irb(main):026:0> surgeon.specialty => "Dermatologist" irb(main):027:0> surgeon.does_surgery? => true |
As with the doctor object, you are still able to access the surgeon’s object attributes thanks to the accessor methods defined in its parent class.
The difference comes when you check whether the surgeon object does surgery. This time it returns true
. This is because the Surgeon
class redefined the does_surgery?
method to provide its own behavior.
Modules
Modules are in short classes that cannot be instantiated. You typically use modules to group together related methods and constants that can either be accessed directly using the scope resolution operator ::
or invoked using the Module object as a receiver, as you can see in the following listing.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
module MyModule SALUTATION = "hello" def self.greet(name) "#{SALUTATION}, #{name}" end end puts MyModule.greet("John Doe") # => "hello, John Doe" puts MyModule::SALUTATION # => "hello" |
Multiple inheritance
Now that you know about inheritance and modules you know the tools to understand this part.
In the Doctor and Surgeon example above you saw how Ruby supports single inheritance, which is the type of inheritance where a child class inherits attributes and methods from a single parent class.
Multiple inheritance is a kind of inheritance in which a child class inherits from multiple parent classes. Is Ruby capable of that? The short answer is no… and the long answer is kind of. Let me explain that to you.
If you look at inheritance in the strict sense of the word, Ruby only supports single inheritance, that is, inheriting from a single parent class.
If you want a class to get functionality from several classes in Ruby, you can achieve that by having that functionality inside separate modules and using mixins.
Module mixins
Mixins allow modules and classes to get new functionalities by including other modules. When a module is included within a class or module definition all the methods, constants and variables from that module are made available to the class or module including it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
module Greeter def greet puts "Hello #{name} #{surname}" end end class Person include Greeter attr_reader :name, :surname def initialize(name, surname) @name = name @surname = surname end end person = Person.new("John", "Doe") #=> #<Person:0x00000001049b5c58 @name="John", @surname="Doe"> person.greet #=> "Hello John Doe" |
There are other ways of using module mixin with extend and prepend, but since I have already talked about that in the past I will give you the link to that post so that you can look at it with more detail.
Should I use inheritance or module mixins?
Let’s be clear, inheritance in Ruby is a very important dependency since, as we saw earlier, there is only one place for it. Once a class inherits from another class, it cannot inherit from another one. It is gone.
When deciding whether to use inheritance or module mixins, I would recommend using mixins in general, unless you are very sure there is an inheritance relationship. Ask yourself, Is class B
really a specialization of class A
? This might seem counterintuitive at first if you come from a language that does not have module mixins, as it happened to me when I started using Ruby, but since Ruby gives you more tools than other programming languages do, you can just choose the best one for the job.
If you enjoyed this post do not forget to subscribe so that you do not miss any of my future updates. If you want me to write about something in particular, you can let me know about it in the comment section below.
See you in my next post! Adiós!