Inheritance
Syntax#
- class SubClass < SuperClass
Refactoring existing classes to use Inheritance
Let’s say we have two classes, Cat
and Dog
.
class Cat
def eat
die unless has_food?
self.food_amount -= 1
self.hungry = false
end
def sound
puts "Meow"
end
end
class Dog
def eat
die unless has_food?
self.food_amount -= 1
self.hungry = false
end
def sound
puts "Woof"
end
end
The eat
method is exactly the same in these two classes. While this works, it is hard to maintain. The problem will get worse if there are more animals with the same eat
method. Inheritance can solve this problem.
class Animal
def eat
die unless has_food?
self.food_amount -= 1
self.hungry = false
end
# No sound method
end
class Cat < Animal
def sound
puts "Meow"
end
end
class Dog < Animal
def sound
puts "Woof"
end
end
We have created a new class, Animal
, and moved our eat
method to that class. Then, we made Cat
and Dog
inherit from this new common superclass. This removes the need for repeating code
Multiple Inheritance
Multiple inheritance is a feature that allows one class to inherit from multiple classes(i.e., more than one parent). Ruby does not support multiple inheritance. It only supports single-inheritance (i.e. class can have only one parent), but you can use composition to build more complex classes using Modules.
Subclasses
Inheritance allows classes to define specific behaviour based on an existing class.
class Animal
def say_hello
'Meep!'
end
def eat
'Yumm!'
end
end
class Dog < Animal
def say_hello
'Woof!'
end
end
spot = Dog.new
spot.say_hello # 'Woof!'
spot.eat # 'Yumm!'
In this example:
Dog
Inherits fromAnimal
, making it a Subclass.Dog
gains both thesay_hello
andeat
methods fromAnimal
.Dog
overrides thesay_hello
method with different functionality.
Mixins
Mixins are a beautiful way to achieve something similar to multiple inheritance. It allows us to inherit or rather include methods defined in a module into a class. These methods can be included as either instance or class methods. The below example depicts this design.
module SampleModule
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def method_static
puts "This is a static method"
end
end
def insta_method
puts "This is an instance method"
end
end
class SampleClass
include SampleModule
end
sc = SampleClass.new
sc.insta_method
prints "This is an instance method"
sc.class.method_static
prints "This is a static method"
What is inherited?
Methods are inherited
class A
def boo; p 'boo' end
end
class B < A; end
b = B.new
b.boo # => 'boo'
Class methods are inherited
class A
def self.boo; p 'boo' end
end
class B < A; end
p B.boo # => 'boo'
Constants are inherited
class A
WOO = 1
end
class B < A; end
p B::WOO # => 1
But beware, they can be overridden:
class B
WOO = WOO + 1
end
p B::WOO # => 2
Instance variables are inherited:
class A
attr_accessor :ho
def initialize
@ho = 'haha'
end
end
class B < A; end
b = B.new
p b.ho # => 'haha'
Beware, if you override the methods that initialize instance variables without calling super
, they will be nil. Continuing from above:
class C < A
def initialize; end
end
c = C.new
p c.ho # => nil
Class instance variables are not inherited:
class A
@foo = 'foo'
class << self
attr_accessor :foo
end
end
class B < A; end
p B.foo # => nil
# The accessor is inherited, since it is a class method
#
B.foo = 'fob' # possible
Class variables aren’t really inherited
They are shared between the base class and all subclasses as 1 variable:
class A
@@foo = 0
def initialize
@@foo += 1
p @@foo
end
end
class B < A;end
a = A.new # => 1
b = B.new # => 2
So continuing from above:
class C < A
def initialize
@@foo = -10
p @@foo
end
end
a = C.new # => -10
b = B.new # => -9