No Ruby, nós não declaramos o tipo de objetos, nem o tipo do retorno de métodos. Embora isso possa parecer algo muito ruim para quem está acostumado com linguagens como Java, linguagens dinamicamente tipadas como o Ruby são muito flexíveis, produtivas e, acredite, seguras. Na maioria das vezes, o medo de não poder contar com o compilador para fazer verificações de tipos não tem fundamento.

Desenvolvedores Ruby estão mais acostumados em definir objetos pelo que eles podem fazer, do que por seu tipo. Esta técnica é chamada de duck typing.

Se anda como um pato e faz barulho como um pato, então deve ser um pato. E o interpretador ficará feliz em fazer com que o objeto seja tratado como um pato. Na prática, isso significa que em vez de fazer verificações de tipo de um objeto, você deve se preocupar se este objeto é capaz de executar o método que você precisa.

Pegue como exemplo strings, arquivos e arrays. As classes Array, File e String implementam o método de instância <<, que quase sempre significa append. Você pode se aproveitar desta interface para criar, por exemplo, uma classe de log que não se importa com o tipo de objeto que irá armazenar esses logs.

class SimpleLogger
  def initialize(io)
    @io = io
  end
 
  def log(message)
    @io << "#{Time.now} - #{message}\n"
  end
end

A classe SimpleLogger consegue enviar os logs para arrays, strings, arquivos e, se quiser, para qualquer outro objeto que implemente o método <<.

O Ruby realmente abraça o duck typing por toda a linguagem. Diversos protocolos exigem que o objeto apenas implemente um método to_<protocol>. Muitas operações que envolvem arrays, por exemplo, exigem que o objeto do lado direito da expressão apenas implemente o método to_ary.

class Numbers
  def to_ary
    [4, 5, 6]
  end
end
 
[1, 2, 3] + Numbers.new
#=> [1, 2, 3, 4, 5, 6]

A classe Hash, por exemplo, permite que você una dois objetos, desde que o método to_hash seja implementado.

class Configuration
  def to_hash
    {root: "/etc"}
  end
end
 
config = Configuration.new
 
{name: "Custom config"}.merge(config)
#=> {:name=>"Custom config", :root=>"/etc"}

Não é preciso dizer que este tipo de protocolo por convenção permite criar códigos muito mais flexíveis, com extrema facilidade.

Isso não significa que saber o tipo de objetos não seja útil. Veja, por exemplo, as operações matemáticas. Qualquer objeto pode ser convertido em números. Para isso, basta implementar o método coerce, que recebe o objeto que está solicitando a coerção. O exemplo abaixo mostra como criar uma classe cuja instância pode ser convertida em números inteiros e flutuantes, mas não em instâncias da classe BigDecimal.

class NumberOne
  def coerce(object)
    case object
    when Integer
      [object, 1]
    when Float
      [object, 1.0]
    else
      raise TypeError, "#{self.inspect} can't be coerced into #{object.class}"
    end
  end
end
 
puts 1 + NumberOne.new
#=> 2
 
puts 1.0 + NumberOne.new
#=> 2.0
 
require "bigdecimal"
puts BigDecimal.new("1.0") + NumberOne.new
#=> TypeError: FakeNumber can't be coerced into BigDecimal

O duck typing vai além de simples regras; é um estilo de programação. Antes de exigir tipos de objetos, pergunte-se se isso é realmente necessário. Às vezes, o tipo do objeto é muito importante, mas muitas vezes isso simplesmente não importa.

NOTA: Este artigo foi tirado do e-book "Conhecendo o Ruby" que estou escrevendo. Se inscreva na newsletter do HOWTO e saiba quando ele for lançado.

Comentários #

#1 devGabriel disse:
02 Jan 12, 07:27PM

Pra quem está engatinhando no Ruby(como eu) essa explicação foi muito boa.

Obrigado (:

#2 Lucas Húngaro disse:
02 Jan 12, 08:07PM

Uma coisa importante a notar é que Ruby é uma linguagem dinâmica e *fortemente* tipada. Muita gente (não é o seu caso) confunde as duas coisas e fica com aquele "medo" de que algo vá dar muito errado em produção porque o compilador não checa tipos. Isso é praticamente impossível de ocorrer já que Ruby tem tipos fortes, embora não exija tipos na passagem de mensagens.

Outra curiosidade à notar é que o duck typing é o ápice do role-based programming, algo essencial para OOP de verdade. Quando você define suas classes deve definir papéis (roles) e não ficar perseguindo representações de coisas do mundo real. Duck typing te dá todo o poder para fazer isso, já que o que realmente interessa na comunicação entre as entidades é o papel de cada uma, e não sua "linhagem" (tipo). Por exemplo: se, ao fazer um cálculo, uma entidade precisa logar a operação, basta que passemos a ela uma entidade que representa o papel de um logger (isso é definido pela sua interface), independente de ser um FileLogger ou um EmailLogger ou qualquer outra coisa. Basta respeitarmos os contratos de comunicação (aka interface, aka protocolo), que estamos mais do que seguros.

Agora que já fiz meu papel de chatão da programação, parabéns pelo texto, tá muito bom. :)

#3 Nando Vieira disse:
02 Jan 12, 09:12PM

Eu não publiquei isso neste artigo, mas antes de falar de Duck Typing no e-book, eu falo sobre Tipagem. Valeu pelo super-duper comentário! \m/

#4 RoaHouse disse:
03 Jan 12, 12:55AM

Pra mim duck typing sempre vai te induzir a pensar em design by contract uma vez que o tipo é "irrelevante", claro que isso pode acabar causando coisas como um tipo tipo Inteiro com um metodo .get_google_search, mas acredito que no geral ao se programar usando o duck typing você acaba naturalmente pensando mais em abstraçoes... e como um dos pilares da OOP são abstrações, imagino que esse seja uma boa ferramenta para designs melhores. imho

#my2cents

#5 Jonathan disse:
05 Jan 12, 09:52PM

Muito bom o texto
Sou iniciante no Ruby e essa abordagem foi explicativa.

Parabéns

PS: curti o lance da musica do post, haha

Deixe um comentário





Não é aceito código HTML: adicione-o no pastie.org ou paste.milk-it.net e poste apenas o link.

Se este é seu primeiro comentário, ele terá que ser aprovado antes de ser exibido.

jQuery: Dominando o framework

Você quer aprender a usar jQuery de verdade? Então chegou a hora! Neste workshop você verá como funciona este framework de JavaScript, entendendo todos os aspectos que fazem do jQuery uma das melhores ferramentas para desenvolvimento de interfaces.

Saiba mais Fechar

Conheça também o HOWTO