Inheritance is a fundamental OOP feature that allows you to create a more specific & specialized version of a 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 superclass 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
Every method & constant defined on
Food will be available on
Fruit, and also
You may think that creating an object hierarchy provides some kind of "feel good" organization for your code, but that's not what this is.
Inheritance is used to create a specialized version of a class.
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!
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.
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.
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.
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.