In Ruby, as in many other
object-oriented languages, inheritance is a mechanism by which a class can inherit properties and methods from another class. The class that inherits is called a subclass, and the class from which it inherits is called the superclass.
Example of Bad Inheritance in Ruby
class Animal
def speak
puts "Animal speaks"
end
end
class Dog < Animal
def speak
puts "Dog barks"
end
end
class Cat < Animal
# This class does not define its own speak method
end
In this example, the
Dog class inherits from the
Animal class, but the
Cat class does not provide its own implementation of the
speak method. If you try to create an instance of the
Cat class and call the
speak method, it will use the implementation from the
Animal class. This can be problematic, as the appropriate response for a cat is
meow.
That is, we expect to get the text 'Cat meows', but we get 'Animal speaks'. It may seem not very critical, but in classes responsible for business logic, this can lead to bigger problems.
We can do the following:
class Cat < Animal
def speak
puts "Cat meows"
end
end
This seems to solve the problem. But imagine there are dozens or hundreds of classes and methods. This approach is not a best practice.
Example of Good Inheritance in Ruby
class Shape
def initialize(color)
@color = color
end
def draw
puts "Drawing a #{@color} shape"
end
end
class Circle < Shape
def initialize(color, radius)
super(color)
@radius = radius
end
def draw
puts "Drawing a #{@color} circle with radius #{@radius}"
end
end
class Square < Shape
def initialize(color, side_length)
super(color)
@side_length = side_length
end
def draw
puts "Drawing a #{@color} square with side length #{@side_length}"
end
end
In this example, there is a
Shape class, which is the base class for all geometric figures. The class has a
color attribute and a
draw method that outputs a drawing message.
The Circle and Square classes inherit from the Shape class and provide their own attributes (radius and side_length) and implementation of the draw method. Each class can call the draw method, but with its own context and data.
This approach allows for creating common properties and methods in the base class, and then extending them in subclasses, providing their own unique logic. This makes the code more readable, maintainable, and flexible.