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.14Without 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.areaAdding 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 6to_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
EasyComplete 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)}"
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Classes and Objects
Working With FilesDefining ClassesInstance VariablesMethods and selfto_s and inspectRecap - Library Card7Polymorphism and Duck Typing
PolymorphismDuck TypingAbstract Methodscase and ClassesRecap - Payment Methods2Accessors and Self
Readers and WritersCustom Settersself in MethodsIdentity and EqualityRecap - Contact List8Special Methods
Equality and hashOperator MethodsIndexing and AppendingCustom CollectionsRecap - Fractions11Project: Library Management
Books and MembersBorrowing Books3Class Methods and Variables
Class MethodsClass Instance VariablesConstantsFactory MethodsRecap - Coupon CodesPractice on your own: Online Ruby compiler