Class inheritance is a fundamental OOP (Object-Oriented Programming) feature that helps you create a more specific & specialized version of any class.
Here’s an example:
Food -> Fruit -> Orange
There’s a relationship between these classes!
We can say that an orange is a fruit, but fruits are also food.
The parent class (also called super class or base class) is always more generic than the subclasses.
Fruit (more generic) is the parent class of
Orange (more specific).
In Ruby it looks like this:
class Food end class Fruit < Food end class Orange < Fruit end
One of the implications of inheritance in Ruby is that every method & every constant defined on
Food will be available on
Fruit, and also on
The methods are passed down from the base class, but not the other way around.
What’s The Purpose of Inheritance?
You may think that creating an object hierarchy provides some kind of “feel good” organization for your code, but there is a lot more to it.
Inheritance is used to create a different version of a parent class which is fit for a particular purpose. Maybe it needs some extra features or methods that wouldn’t make sense in the parent class.
Here’s an example:
All fruits have a color, a weight, and a name.
Some fruits may have special characteristics that aren’t shared with other fruits, so you create a new class that inherits the characteristics of ALL fruits (color, weight, etc.) & then you add the special characteristic.
That’s what I mean by specialization.
You have a class that writes to a database, but you want another version of the class (perhaps for debugging purposes) which logs all the database operations.
In this case, you want the decorator pattern.
You can read a detailed explanation on this article, but the basic idea is that you use inheritance to wrap another class & then add something new to it.
Without having to change the original!
Inheritance in The Real World
We have learned about inheritance, but did you know that you’re using it every day as a Ruby developer?
Ruby itself uses inheritance to enable methods like:
This is because all Ruby objects inherit from the
Object class by default.
So if you create a class like this:
class Apple end
Its parent class is
Apple.superclass # Object
That’s why you’re able to use methods like the ones mentioned above.
For example, when you call
puts Ruby looks for this method in your class.
- It looks for the method in one of the parent classes
- If not found, it starts from the object again & it’ll try to find
- If not found, it will raise a
NameErrorif the method was called without an explicit object (
ais the explicit object)
You can find more examples of inheritance in Rails.
class ApplicationController < ActionController::Base end
class SessionsController < ApplicationController end
class Comment < ApplicationRecord belongs_to :article end
Inheritance is everywhere in Ruby, but sometimes it isn’t the right solution.
Composition: An Alternative to Inheritance
Inheritance has certain limitations.
You want to build a computer from parts.
We say that the computer has parts, but the individual parts aren’t computers by themselves.
If you take them apart they can’t do their function.
We need something else…
We need composition!
Composition builds classes where different parts come together to perform a function.
Just like a computer.
Here’s an example of composition in action:
class Computer def initialize(memory, disk, cpu) @memory = memory @disk = disk @cpu = cpu end end
The computer is given the parts it needs to work.
This is composition.
The Liskov Substitution Principle
Inheritance is powerful when used in the right situations.
But like all tools it can be abused!
In fact, there is a popular quote from the original Design Patterns book that goes like this:
“Prefer composition over inheritance.”
Design Patterns: Elements of Reusable Object-Oriented Software
To make sure you’re using inheritance correctly there is one principle you can follow, the
L from SOLID.
It stands for “Liskov Substitution Principle”.
This says that your subclasses must be able to be used in place of your base class.
In other words:
If you inherit from
Fruit & the
color is a string, you don’t want to change the
color to return a symbol in a subclass.
Fruit depend on
color returning a string.
class Fruit def color "orange" end end
This breaks LSP:
class Orange < Fruit def color :orange end end
If you change
color to return a symbol, then you can’t replace a
Fruit object with an
Because if you call a method like
split on a symbol you’ll get an error.
It’s a method that symbols don’t have.
But strings do.
Another red flag is when your subclass is not a true specialization of the parent class & you’re just using the parent class to share utility methods.
You’ve learned about inheritance & composition in Ruby!
You can use inheritance to create a specialized version of a parent class, and composition to put together components into a whole. Remember to follow the
LSP principle if you don’t want to make a mess.
Now you can write better Object-Oriented Code 🙂
Thanks for reading.