Interfaces in Ruby
Interfaces are named collections of method signatures. In Ruby, we use modules to define interfaces, although they are not strictly enforced like in some other languages.
require 'math'
# Here's a basic interface for geometric shapes.
module Geometry
def area
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
def perim
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
end
# For our example we'll implement this interface on
# `Rect` and `Circle` classes.
class Rect
include Geometry
attr_reader :width, :height
def initialize(width, height)
@width = width
@height = height
end
# To implement an interface in Ruby, we just need to
# implement all the methods in the module. Here we
# implement `Geometry` on `Rect`.
def area
@width * @height
end
def perim
2 * @width + 2 * @height
end
end
class Circle
include Geometry
attr_reader :radius
def initialize(radius)
@radius = radius
end
# The implementation for `Circle`.
def area
Math::PI * @radius * @radius
end
def perim
2 * Math::PI * @radius
end
end
# If an object includes a module, then we can call
# methods that are in the named module. Here's a
# generic `measure` method taking advantage of this
# to work on any `Geometry`.
def measure(g)
puts g.inspect
puts g.area
puts g.perim
end
# Main program
r = Rect.new(3, 4)
c = Circle.new(5)
# The `Circle` and `Rect` classes both
# include the `Geometry` module so we can use
# instances of these classes as arguments to `measure`.
measure(r)
measure(c)
To run the program:
$ ruby interfaces.rb
#<Rect:0x00007f8b9a8b0a98 @width=3, @height=4>
12.0
14.0
#<Circle:0x00007f8b9a8b0a20 @radius=5>
78.53981633974483
31.41592653589793
In Ruby, we use modules to define interfaces. While Ruby doesn’t enforce interface implementation as strictly as some other languages, it’s a common practice to raise a NotImplementedError
for methods that should be implemented by including classes.
The include
keyword in Ruby is used to add methods from a module to a class, which is similar to implementing an interface in other languages.
Ruby’s dynamic nature allows for duck typing, which means that as long as an object responds to the methods called on it, it can be used regardless of its type. This is why we can pass both Rect
and Circle
instances to the measure
method without explicitly declaring that they implement a specific interface.
To learn more about Ruby’s modules and how they can be used to create interfaces, check out the Ruby documentation on modules.