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.

JavaScript Avançado

O JavaScript é a única linguagem que muitos acreditam saber sem nunca terem parado para realmente aprendê-la. Neste workshop rápido você entenderá de verdade todos os conceitos avançados do JavaScript em 4 horas puramente práticas.

Saiba mais Fechar

Conheça também o HOWTO