Menu
Coddy logo textTech

Duck Typing

Parte da seção Programação orientada a objetos do Journey de Ruby da Coddy. Lição 33 de 57.

Ruby não verifica a classe de um objeto antes de chamar um método; ele só precisa que o objeto tenha o método. "Se anda como um pato e grasna como um pato, é um pato." Isso é tipagem pato: classes que não compartilham nenhuma superclasse ainda podem ser usadas no mesmo lugar:

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 }

Saída:

Quack
Beep
...news...

As classes integradas também participam. Um método que usa apenas << funciona com um array, uma string ou qualquer objeto seu que defina <<:

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).count

Saída:

["1", "2"]
"12"
3

respond_to?(:name) verifica se um objeto possui um método público, sem chamá-lo. Use-o quando um objeto pode ou não oferecer suporte a um recurso opcional:

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
end

Saída:

printing report
Scanner cannot print

Quando o objeto não possui o método, a chamada gera NoMethodError, cuja mensagem nomeia o método e o objeto. A tipagem pato depende de que todo objeto passado responda aos métodos que lhe são enviados:

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
end

Saída:

Meow
undefined method `speak' for an instance of Integer
challenge icon

Desafio

Fácil

Complete as classes Printer e LineCounter e o auxiliar export(items, target) em functions.rb. export envia cada item ao destino com target << item.to_s e retorna o destino. Printer#<< imprime o texto com um número de linha, 1: text, e retorna a impressora. LineCounter#<< conta linhas e caracteres e retorna o contador; seu to_s retorna 3 lines, 12 chars.

O código fornecido lê os itens, exporta-os para um array (impresso com p), para uma nova string (impressa com p), para um Printer e para um LineCounter (impresso com puts).

Seu código deve ser colocado em printer.rb, line_counter.rb e functions.rb. main.rb contém o código fornecido de entrada/saída e não pode ser editado.

Experimente você mesmo

require_relative 'printer'
require_relative 'line_counter'
require_relative 'functions'

# Código de entrada/saída fornecido: mantenha-o como está
input = []
while (line = gets)
  input << line.chomp
end
p export(input, [])
p export(input, +"")
export(input, Printer.new)
puts export(input, LineCounter.new)
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação orientada a objetos

Pratique por conta própria: Compilador de Ruby online