Confession time: I’m kind of in love with object-oriented programming.
I should probably clarify that I don’t always understand OOP, and I sometimes even confuse myself for no good reason whenever the topic of “self” comes up. But hey, every relationship has its ups and downs, and I’ve only really been committed to this one for the past week or so.
OOP is easy to understand mostly because of its strong connection to real life – everything is an object, all objects have properties/traits/characteristics, so on and so forth. But dealing with multiple types of objects across different types of classes gets real tricky, real fast. This especially holds true when it comes to class inheritance.
WTF is inheritance?
Inheritance in object-oriented programming is pretty much exactly what it sounds like: when one class inherits the properties of another class, just like how you inherit traits from your parents (even if you might wish otherwise).
Okay, maybe an example here will help:
Imagine that you have a class called BakedStuff
, which includes all the baked goods your heart could ever desire. Every instance (every object) of this BakedStuff
class has a name
, baking time
, and baking_temperature
, which we’ll set to default at 375 degrees.
And for good measure, let’s also throw in a little method in there called bake_for
, which is just going to return a string that will tell us how much time to bake our treats for, and at what temperature.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Nothing too complicated about it, right?
Now I can create an actual object and pass in the correct parameters. I can even ask the cake how long I should bake it for, which would look something like this:
1 2 |
|
would output: To make cake, bake for 30 min at 375 degrees Fahrenheit.
Awesome. I love cake. But you know what I like even more than cake? Cookies! Just like in my first class, I want to make a new Cookie
class, which will also have a name, time, baking temperature, and bake_for
method. Now, I could theoretically rewrite all of my code from the BakedStuff
class, but then I’d have less time to bake and I’m also pretty sure that goes against the whole “DRY” concept in Ruby.
What I could do is have my new Cookie
class inherit everything from my BakedStuff
class, since I want all my new instances of Cookie
to share the same methods as my BakedStuff
class.
Let me try it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Whoa! New code!
- That
<
sign is what we use to denote inheritance. All it means is that myCookie
class is now inheriting all the methods from myBakedStuff
class. - Notice that this class has some additional features, including a new instance method that sets the
cookies_made
. - But wait, what’s that
super
thing doing in there??
WHUT is super
?
The super
keyword in Ruby is, well, pretty super. Okay, okay, what it really does is allow you to call methods up the “inheritance hierarchy”, which means that you can use methods that were previously defined in classes that this class now inherits from.
In fact, super
is what allows me to pull down everything from my initialize
and bake_for
methods in my parent class, which in this case is my BakedStuff
class. You’ll also notice that I’ve reassigned my @time
variable to equal 20 minutes, and that I’ve added a string interpolation into my bake_for
method. And I didn’t have to retype any of the code from my parent class in the process!
Let’s look at what an object of the Cookie class could look like:
1 2 |
|
would output: To make cookies, bake for 20 min at 400 degrees fahrenheit. This recipe will yield 15 cookies.
Okay, one last example. Let’s say that my favorite kind of cookie is a Snickerdoodle. I want it to inherit from its parent class, Cookie
, but I also want to jazz the Snickerdoodle
class up a little bit. I’m going to change its bake_for
method and make a little counter that will tell me exactly how many snickerdoodles I’ve got left.
This would look something like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Now let me actually create a batch of snickerdoodles:
1 2 |
|
which outputs:
1 2 3 4 5 6 7 8 9 10 |
|
Now for the tricky part. What do you think will happen if I create a new batch of cookies in my Cookie
class? Do you think I’ll get a cookie counter? Do you think my Cookie
class will be affected by my Snickerdoodle
class? Let’s try it and see:
1 2 |
|
which outputs: To make chocolate chip cookies, bake for 20 min at 375 degrees Fahrenheit. This recipe will yield 24 cookies.
And what about if I create a new pie?
1 2 |
|
outputs: To make pie, bake for 45 min at 450 degrees Fahrenheit.
Interesting. It doesn’t seem to matter at all that I changed my Cookie
class – BakedStuff
stayed the same. And all the edits I made to my Snickerdoodle
class didn’t fall up to my Cookie
class, either. That’s because inheritance only works in one direction, from the parent class, down to its children.
I should also mention that super
didn’t change my parent classes either; all it did was bring the already-created methods from my parent classes down to its children. It’s actually passing all the arguments to the parent class’ method.
Finally, it’s crucial to remember that a class can only inherit from one class at a time. Something like Snickerdoodle < Cookie < BakedStuff
would have raised an error. Also, remember that instance variables have nothing to do with class inheritance, since they are not defined by the class itself!