Menu
Coddy logo textTech

Polymorphism

Part of the Object Oriented Programming section of Coddy's Ruby journey. Lesson 32 of 57.

Polymorphism means that one method call works on objects of different classes, and each object answers in its own way. Code that loops over shapes calls area on each without asking what kind of shape it has:

class Shape
  def to_s
    format("%s %.2f", self.class.name.downcase, area)
  end
end

class Circle < Shape
  def initialize(r)
    @r = r
  end

  def area
    3.14159 * @r * @r
  end
end

class Square < Shape
  def initialize(side)
    @side = side
  end

  def area
    @side * @side
  end
end

shapes = [Circle.new(1), Square.new(3)]
shapes.each { |s| puts s }
puts format("total %.2f", shapes.sum(&:area))

Output:

circle 3.14
square 9.00
total 12.14

Without polymorphism, the caller has to check the kind of every object and repeat that check wherever the difference matters. With it, the difference lives inside each class:

# without polymorphism
case shape.kind
when "circle" then 3.14159 * shape.r ** 2
when "square" then shape.side ** 2
end

# with polymorphism
shape.area

Adding a new kind of object then needs no change to the calling code: a new subclass with its own area works in every loop that already exists:

class Shape
  def describe
    "area #{area}"
  end
end

class Square < Shape
  def area
    4
  end
end

class Triangle < Shape
  def area
    6
  end
end

[Square.new, Triangle.new].each { |s| puts s.describe }

Output:

area 4
area 6

to_s is the everyday example: puts calls it on any object, and every class answers differently. Your classes join in by defining their own to_s:

class Coin
  def to_s
    "a coin"
  end
end

[42, "text", nil, [1, 2], Coin.new].each { |x| puts "[#{x}]" }

Output:

[42]
[text]
[]
[[1, 2]]
[a coin]
challenge icon

Challenge

Easy

Complete the shapes. Shape#to_s returns circle: 3.14, the class name in lower case and the area with two decimals, and every subclass provides area: Circle.new(r) (use 3.14159 for pi), Rectangle.new(w, h) and Triangle.new(base, height).

The supplied code reads lines circle 1, rectangle 2 3 or triangle 4 5, prints every shape, then total: 22.14 and largest: rectangle: 6.00, working only with to_s and area.

Your code goes in shape.rb, circle.rb, rectangle.rb and triangle.rb. main.rb holds the supplied input/output code and cannot be edited.

Try it yourself

require_relative 'shape'
require_relative 'circle'
require_relative 'rectangle'
require_relative 'triangle'

# Supplied input/output code: keep it as it is
input = []
while (line = gets)
  input << line.chomp
end
kinds = { "circle" => Circle, "rectangle" => Rectangle, "triangle" => Triangle }
shapes = input.map do |line|
  kind, *numbers = line.split
  kinds[kind].new(*numbers.map(&:to_f))
end
shapes.each { |s| puts s }
puts format("total: %.2f", shapes.sum(&:area))
puts "largest: #{shapes.max_by(&:area)}"
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming

Practice on your own: Online Ruby compiler