- Read Tutorial
- Watch Guide Video
In this lesson, we are going to learn about an important object-oriented principle called inheritance. Before going into how it is executed in Ruby, let's see why it's important for building applications.
To start with, inheritance means your classes can have a hierarchy. It is best used when different classes have some shared responsibilities, since it would be a poor practice to duplicate code in each class for identical or even similar behavior.
For example, we have been working on our ApiConnector
class over the last few lessons. Let's say we have different API classes for various platforms, but each class shares a number of common data or processes. Instead of duplicating code in each of the API connector classes, we can have one parent class with the shared data and methods. From there we can create child classes from this parent class. With the way that inheritance works, each of the child classes will have access to the components provided from the parent class.
For example, we have four APIs, namely, SmsConnector
, PhoneConnector
, MailerConnector
and XyzConnector
. If we wrote code individually for each of these classes, it would like this:
class SmsConnector def initialize(title:, description:, url: 'google.com') @title = title @description = description @url = url end def send_sms puts "Sending SMS message..." end end class MailerConnector def initialize(title:, description:, url: 'google.com') @title = title @description = description @url = url end def send_mail puts "Sending mail message..." end end class PhoneConnector def initialize(title:, description:, url: 'google.com') @title = title @description = description @url = url end def place_call puts "Placing phone call..." end end class XyzConnector def initialize(title:, description:, url: 'google.com') @title = title @description = description @url = url end def does_something_else puts "Secret stuff..." end end
If you see, we are simply repeating the same code across different classes. This is considered a poor programming practice.
Instead, we can make an ApiConnector
parent class, and the each of the other classes can inherit the common functionality from this class.
class ApiConnector def initialize(title:, description:, url: 'google.com') @title = title @description = description @url = url end end class SmsConnector < ApiConnector def send_sms puts "Sending SMS message..." end end class MailerConnector < ApiConnector def send_mail puts "Sending mail message..." end end class PhoneConnector < ApiConnector def place_call puts "Placing phone call..." end end class XyzConnector < ApiConnector def does_something_else puts "Secret stuff..." end end
By leveraging inheritance we were able to cut all of the duplicate code throughout our classes.
The syntax for using inheritance is to define the class name followed by the symbol <
, followed by the parent class name. For example our SmsConnector
class inherits from the ApiConnector
with the following syntax:
class SmsConnector < ApiConnector
Each of these child classes now have access to the full set of elements provided in the parent ApiConnector
class. We can test this out by adding the attributes to the send_sms
method inside of the SmsConnector
class, like this:
class SmsConnector < ApiConnector def send_sms puts "Sending SMS message with the #{@title} and #{@description}" end end
Now if we create a new instance of SmsConnector
with the following parameters we can call the send_sms
method:
sms = SmsConnector.new(title: "Hi there!", description: "I'm in a SMS message") sms.send_sms
Running this code will give the following output:
Sending SMS message with the Hi there! and I'm in a SMS message
A rule of thumb in OOP is to ensure that a class performs a single responsibility. For example, the ApiConnector
class should not send sms messages, make phone calls or send emails since that would three core responsibilities. Our file is taking a better strategy by creating child class for each of these functionalities.
For practice you can create new instances of each class and then call each of the methods present inside the respective classes.
The code to instantiate each of the classes and call the methods is here:
sms = SmsConnector.new(title: "Hi there!", description: "I'm in a SMS message") mail = MailerConnector.new(title: "Hi there!", description: "I'm in an email message") phone = PhoneConnector.new(title: "Hi there!", description: "I'm on a call") xyz = XyzConnector.new(title: "Hi there!", description: "Who knows what I'm in") sms.send_sms mail.send_mail phone.place_call xyz.does_something_else
So, that's how inheritance works in Ruby. The key to remember about inheritance is that it gives you the ability to clean up your codebase and created inherited classes that share common behavior.