The past few articles briefly covered how classes can help our program and ourselves. Mainly through code organization, and eliminating unnecessary duplication through encapsulation. We’ve also discussed how all the methods in a class are automatically accessible to instances of that class, or ‘objects’.
Today, I want to look at two ways you can share information between classes in Ruby. Class-level inheritance and mixins.
Inheritance allows you to build a class thats a refinement of another class. We can refer to a class that is extended from another class as a subclass of the class it is extended from. The base class can be called a superclass of its subclass, or a parent of its descendent class.
When the act of subclassing happens, the ‘child’ class inherits all of the capabilities of its ‘parent’ class, meaning all of the parent’s instance methods will be available to the objects of the child class.
Here’s a quick example of what that could look like:
The parent class in the example above defines one instance method ‘greeting’, and the descendent class first creates an object named ‘jill’, and then calls the ‘greeting’ method listed in its super class.
When you call a method with an object from a descendent class and the method definition isn’t listed in that class, Ruby automatically looks above, into the parent class. If the method isn’t defined in the parent class then Ruby will continue to head upward in the parent of the parent class and etc… Until it runs out of classes to check.
Modules are a way of grouping together methods, classes, and constants. I won’t head too far into the benefits of modules, but I think its good to mention nonetheless.
Modules offer namespaces to prevent name clashes, which helps organize when our chunks of code grow over time. A namespace is essentially a sandbox where your methods can play without having to worry about clashing with other methods with the same name.
Modules have another use which can basically remove the need for inheritance, by something called a mixin. One of the trade-offs of modules is that modules don’t have instances of themselves, because a module isn’t a class. But you can ‘include’ a module within a class definition. Doing so makes all the module’s instance methods available to the class that it was included in.
Here’s an example:
In the example above I include the PeanutButter module into my Toast class, which makes a reference to the module and allows access to the instance methods from the module.