Duck Typing
Part of the Object Oriented Programming section of Coddy's Ruby journey. Lesson 33 of 57.
Ruby does not check an object's class before calling a method; it only needs the object to have the method. "If it walks like a duck and quacks like a duck, it is a duck." This is duck typing: classes that share no superclass can still be used in the same place:
class Duck
def speak
"Quack"
end
end
class Robot
def speak
"Beep"
end
end
class Radio
def speak
"...news..."
end
end
[Duck.new, Robot.new, Radio.new].each { |thing| puts thing.speak }Output:
Quack
Beep
...news...Built-in classes take part too. A method that only uses << works with an array, a string or any object of yours that defines <<:
class Tally
attr_reader :count
def initialize
@count = 0
end
def <<(_item)
@count += 1
self
end
end
def export(items, target)
items.each { |item| target << item.to_s }
target
end
p export([1, 2], [])
p export([1, 2], +"")
puts export([1, 2, 3], Tally.new).countOutput:
["1", "2"]
"12"
3respond_to?(:name) asks whether an object has a public method, without calling it. Use it when an object may or may not support an optional feature:
class Printer
def print_page(text)
"printing #{text}"
end
end
class Scanner
end
[Printer.new, Scanner.new].each do |device|
if device.respond_to?(:print_page)
puts device.print_page("report")
else
puts "#{device.class} cannot print"
end
endOutput:
printing report
Scanner cannot printWhen the object does not have the method, the call raises NoMethodError, whose message names the method and the object. Duck typing relies on every object that is passed in answering the methods it is sent:
def loudest(things)
things.map(&:speak).max_by(&:size)
end
class Cat
def speak
"Meow"
end
end
puts loudest([Cat.new, Cat.new])
begin
loudest([Cat.new, 42])
rescue NoMethodError => e
puts e.message
endOutput:
Meow
undefined method `speak' for an instance of IntegerChallenge
EasyComplete the classes Printer and LineCounter and the helper export(items, target) in functions.rb. export sends every item to the target with target << item.to_s and returns the target. Printer#<< puts the text with a line number, 1: text, and returns the printer. LineCounter#<< counts lines and characters and returns the counter; its to_s returns 3 lines, 12 chars.
The supplied code reads items, exports them to an array (printed with p), to a new string (printed with p), to a Printer and to a LineCounter (printed with puts).
Your code goes in printer.rb, line_counter.rb and functions.rb. main.rb holds the supplied input/output code and cannot be edited.
Try it yourself
require_relative 'printer'
require_relative 'line_counter'
require_relative 'functions'
# Supplied input/output code: keep it as it is
input = []
while (line = gets)
input << line.chomp
end
p export(input, [])
p export(input, +"")
export(input, Printer.new)
puts export(input, LineCounter.new)
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