No artigo anterior eu mostrei como o self muda em diferentes contextos. Neste artigo, você verá o que são as metaclasses e como elas podem ser úteis na metaprogramação.

Entendendo classes Singleton

Todo objeto do Ruby está associado a duas classes: a classe que a instanciou e uma classe anônima, escondida, específica do objeto. Esta classe anônima é chamada de Singleton Class, mas antes de ter um nome oficial também era chamada de anonymous class, metaclass, eigenclass ou ghost class.

O nome Singleton usado pelo Ruby nada tem a ver com o Singleton Pattern, que também está disponível com a biblioteca Singleton.

A sintaxe mais comum para acessar a classe Singleton é

class << object
end

onde object é o objeto cuja classe Singleton você quer. É muito comum vermos algo como o exemplo à seguir para definir métodos em uma classe.

class Person
  class << self
    def count
      @count ||= 0
    end
  end
end

Aqui, estamos definindo um método na classe Singleton do objeto Person (lembre-se: tudo no Ruby é objeto, inclusive classes). Como consequência, isso irá definir o método Person.count. O efeito é exatamente o mesmo que

class Person
  def self.count
    @count ||= 0
  end
end

No Ruby 1.9.2, foi adicionado o método Object#singleton_class, que é apenas um atalho para a sintaxe class << self; self; end. Em versões mais antigas, você pode injetar este método com

class Object
  def singleton_class
    class << self; self; end
  end unless respond_to?(:singleton_class)
end

Toda vez que injeta métodos em um objeto, eles são adicionados como métodos singleton. O que é realmente importante saber é que estes métodos pertecem unicamente ao objeto em que foram definidos, não afetando nenhum outro objeto da hieraquia.

string = "Hi there!"
another_string = "Hi there!"
 
def string.to_yo
  self.gsub(/\b(Hi|Hello)( there)\b?!?/, "Yo! Wassup?")
end
 
string.to_yo
#=> "Yo! Wassup?"
 
another_string.respond_to?(:to_yo)
#=> false

E para provar que o método to_yo é singleton, podemos utilizar o método Object#singleton_methods.

string.singleton_methods
#=> ["to_yo"]
 
another_string.singleton_methods
#=> []

Você também pode adicionar métodos singleton de outras maneiras. Uma delas é estendendo um objeto com um módulo.

module Extension
  def sum
    self.inject(0) {|sum, number| sum + number}
  end
end
 
numbers = [1,2,3]
numbers.extend Extension
numbers.singleton_methods
#=> ["sum"]

Outra maneira é usando a própria classe Singleton.

numbers = [1,2,3]
 
class << numbers
  def sum
    self.inject(0) {|sum, number| sum + number}
  end
end
 
numbers.singleton_methods
#=> ["sum"]

Ou ainda, executando código no contexto do objeto.

numbers = [1,2,3]
 
numbers.instance_eval do
  def sum
    self.inject(0) {|sum, number| sum + number}
  end
end
 
numbers.singleton_methods
#=> ["sum"]

Quando você cria uma classe Singleton em um objeto, não poderá mais utilizar o método Marshal.dump, já que a biblioteca Marshal não suporta objetos com classes Singleton (ela irá lançar a exceção TypeError: singleton can't be dumped). A única maneira de fazer isso e ainda poder utilizar o Marshal é utilizando o método Object#extend.

Agora, sabendo que você pode adicionar métodos em um objeto com uma sintaxe como def object.some_method; end, perceba que é exatamente isso que fazemos quando definimos um método em uma classe; a única diferença é que passamos o próprio self.

class Person
  def self.say_hello
    "Hello there!"
  end
end
 
Person.singleton_methods
#=> ["say_hello"]

Com base nesse exemplo, é possível afirmar que métodos de classe não existem no Ruby! Pelo menos não no sentido de métodos estáticos! O que acontece é que estes métodos pertencem a um objeto, que por acaso é uma classe.

Finalizando

No próximo artigo veremos mais sobre definição dinâmica e execução de métodos.

Comentários #

#1 Ruby Object Model – self — Simples Ideias. Por Nando Viei... disse:
30 Set 10, 10:32AM

[...] próximo artigo, irei falar sobre metaclasses do Ruby. Até [...]

#2 Tweets that mention Ruby Object Model – Singleton Class — Sim... disse:
30 Set 10, 10:58AM

[...] This post was mentioned on Twitter by tapajos, Ozéias Sant'ana, Rafael Rosa, Tino Gomes, Alberto Leal and others. Alberto Leal said: RT @ozeias: RT @fnando: segundo artigo sobre ruby object model, agora abordando as singleton classes do ruby. ~ http://bit.ly/d8GPXY [...]

#3 Saulo Brust disse:
30 Set 10, 11:00AM

Excelente esta série de artigos, está clareando bastante minha mente no aprendizado de Ruby.

#4 Emerson Vinicius disse:
30 Set 10, 11:15AM

@Nando existe alguma perda de performance nesses diferente modos de adicionar métodos singleton?

#5 Dann Luciano disse:
30 Set 10, 11:32AM

Nando uma sugestão bem legal, seria colocar exemplos reais dos conceitos apresentados, por exemplo como o rails ou outro framework implementa na pratica.

#6 Nando Vieira disse:
30 Set 10, 12:47PM

A ideia é agrupar todos os conceitos que estou mostrando quando eu começar a falar de Metaprogramming. Exemplos reais normalmente são compostos por mais de uma técnica, e isso dificultaria uma explicação mais focada e simples.

#7 Nando Vieira disse:
30 Set 10, 12:49PM

Boa pergunta. Não cheguei a fazer nenhum benchmark nesse sentido! Acredito que se houver, a diferença não deve ser tão gritante.

#8 Daniel disse:
30 Set 10, 02:16PM

Nessa questão de performance, vale lembrar que classes singleton trazem todo o custo de declarar uma classe nova (consumo de memória, "poluição" dos caches de invocação de método) para beneficiar um único objeto. Internamente, na primeira vez que se requer a singleton, o Ruby apenas altera o ponteiro de classe do objeto para uma nova anônima que herda da classe original (mais ou menos como: `x.class = Class.new(x.class)`)

Sendo assim, de maneira geral não é uma boa idéia usar singleton classes em objetos "de massa", criar uma subclasse explícita é quase sempre uma idéia melhor. Para objetos que são instanciados apenas uma vez (ou seja, essencialmente singletons), como classes, não há diferença prática.

#9 Adimir Colen disse:
30 Set 10, 02:50PM

Legal, bem bacana esse "diferença" entre singletons de pattern.
Parabéns pelos posts.

#10 Maurício Szabo disse:
30 Set 10, 03:29PM

Isso me lembra um post meu, antigo, falando sobre exatamente o mesmo assunto. Talvez esteja um pouco mais confuso, mas serve talvez pra ilustrar um pouco (tentei colocar uns exemplos lá):

http://mauricioszabo.wordpress.com/2010/06/01/o-que-e-eingenclass-afinal-2/

No mais, parabéns pelo post!

#11 mairon disse:
02 Out 10, 12:16PM

muitoooo bom
abraco

#12 mairon disse:
12 Out 10, 08:08PM

perfect

abraco

#13 Ricardo Borghetti disse:
06 Nov 10, 09:27PM

Muito bom!
Obrigado por compartilhar este conhecimento conosco!
[]'s

#14 Classe singleton e definição de métodos de classe — Simp... disse:
23 Out 11, 07:33PM

[...] falei sobre classes singleton no Ruby, mostrei como era possível atuar no contexto da classe. Uma coisa muito comum entre os [...]

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