Web,ruby, Ajax ou qualquer outra coisa que me venha a cabeça (com prioridade para esta última)

10 janeiro, 2007

Variáveis de instância de classe - Ruby

Isto é uma tradução de um artigo de Martin Fowler sobre ClassInstanceVariable (uma variável de instância de classe).

Quando se aprende algo sobre objectos normalmente aprendemos que esses objectos capturam dois tipos de dados: instância (exemplar) e classe. As variáveis de instância são as mais comuns, os dados variam de instância para instância do objecto. As variáveis de classe, também conhecidas por variávies estáticas, são partilhadas por todas as instâncias (exemplares) da classe. Cada instância aponta para o mesmo valor e qualquer alteração é vista por todas as instâncias. As variáveis de classe são muito menos comuns do que as variáveis de instância especialmente variáveis de classe que sejam mutáveis.

Um dos aspectos complexos das variáveis de classe é como é que elas interagem com uma hierárquia de herança. Consideremos uma variável de classe que é usada para guardar a própria instância.

  1. class Empregado
  2.  @@exemplares = []
  3.  def self.exemplares
  4.   return @@exemplares
  5.  end
  6.  def guardar
  7.   @@exemplares << self
  8.  end
  9.  def initialize nome
  10.   @nome = nome
  11.  end
  12. end
  13.  
  14. Empregado.new("Martins").guardar
  15. Empregado.new("Roberta").guardar
  16. Empregado.new("Eurico").guardar
  17.  
  18. puts Empregado.exemplares.size

Não há aqui nenhuma surpresa, há três empregados. Agora experimentemos isto:

  1. class Empregado
  2.  @@exemplares = []
  3.  def self.exemplares
  4.    @@exemplares
  5.  end
  6.  def guardar
  7.   @@exemplares << self
  8.  end
  9.  def initialize nome
  10.   @nome = nome
  11.  end
  12. end
  13.  
  14. class Programador < Empregado; end
  15. class Restodopessoal < Empregado; end
  16.  
  17. Restodopessoal.new('Martins').guardar
  18. Restodopessoal.new('Roberta').guardar
  19. Programador.new('Eurico').guardar
  20.  
  21. puts Restodopessoal.exemplares.size
  22. puts Programador.exemplares.size

O resultado é 3 e 3, enquanto preferiamos obter 2 e 1. A razão por que isto se passa é que a variável de classe é partilhada ao longo de todas as instâncias da classe e essas incluem todas as subclasses. Há duas classes mas só uma variável.

Por vezes esta variável que perpassa a totalidade da hieráquia é o que necessitamos, mas por vezes, como neste caso, preferiamos ter uma variável diferente em cada classe. Podemo-nos referir a uma variável de instância de classe do mesmo modo que a uma variável de classe, mas teremos um valor diferente por classe.

O suporte para variáveis de instâncias de classe não é comum nas linguagens OO, mas não é difícil de criarmos um nós próprios. O modo óbvio de o criar seria o de usar um dicionários de chaves por nome de classe.

  1. class Empregado
  2.  @@exemplares = {}
  3.  def self.exemplares
  4.   @@exemplares[self]
  5.  end
  6.  def guardar
  7.   @@exemplares[self.class] || = []
  8.   @@exemplares[self.class] || = << self
  9.  end
  10.  def initialize nome
  11.   @nome = nome
  12.  end
  13. end
  14.  
  15. class Programador < Empregado; end
  16. class Restodopessoal < Empregado; end
  17.  
  18. Restodopessoal.new('Martins').guardar
  19. Restodopessoal.new('Roberta').guardar
  20. Programador.new('Eurico').guardar
  21.  
  22. puts Restodopessoal.exemplares.size
  23. puts Programador.exemplares.size

Pode usar esta técnica em qualquer linguagem OO. Ruby contudo tem de facto variáveis de instância de classe.

  1. class Empregado
  2.  class << self; attr_accessor :exemplares; end
  3.  def guardar
  4.   self.class.exemplares || = []
  5.   self.class.exemplares << self
  6.  end
  7.  def initialize nome
  8.   @nome = nome
  9.  end
  10. end
  11.  
  12. class Programador < Empregado; end
  13. class Restodopessoal < Empregado; end
  14.  
  15. Restodopessoal.new('Martins').guardar
  16. Restodopessoal.new('Roberta').guardar
  17. Programador.new('Eurico').guardar
  18.  
  19. puts Restodopessoal.exemplares.size
  20. puts Programador.exemplares.size

A definição da variável de instância de classe é o fragmento class << self; attr_accessor :exemplares; end.

2 comentários:

Anónimo disse...

A simbiose entre o Ruby e o Rails é tão intensa que muitos pensam que Ruby on Rails é uma linguagem e por isso é muito difícil encontrar material de qualidade sobre Ruby(sem Rails). Boa a sua contribuição.

Carlos Afonso disse...

Há muito tempo que não passeio por linguagens de programação, parece ter chegado o momento para voltar a falar das mesmas.