Go to English Blog

Classe singleton e definição de métodos de classe

Leia em 1 minuto

Quando falei sobre classes singleton no Ruby, mostrei como era possível atuar no contexto da classe. Uma coisa muito comum entre os desenvolvedores é definir métodos de classe através da classe singleton.

class Person
  class << self
    def description
      "It defines an user"
    end
  end
end

No exemplo acima estamos definindo uma método de classe Person.description. Infelizmente, quando a quantidade de métodos é muito grande, a legibilidade fica prejudicada; você não consegue saber rapidamente se este método é de classe ou instância.

Na maioria das vezes essa escolha é feita sem um objetivo; a explicação é que é possível salvar alguns caracteres ao não digitar self de novo e de novo.

Particularmente, acho que o código acima fica muito mais legível quando escrito desta forma:

class Person
  def self.description
    "It defines an user"
  end
end

Isso não significa que você não deva usar nunca a classe singleton. Eu uso constantemente. Mas não para definir métodos dessa maneira. Eu gosto, por exemplo, de definir atributos.

class Config
  class << self
    attr_accessor :root_dir
  end
end

Um outro uso é quando preciso modificar alguma classe quando um hook é executado, como em plugins do ActiveRecord.

module MyPlugin
  module ClassMethods; end
  module InstanceMethods; end

  def self.extended(base)
    class << base
      include InstanceMethods
      extend ClassMethods
    end
  end
end

ActiveRecord::Base.extend(MyPlugin)

O método Module#include é privado. Uma alternativa seria injetar este módulo com o método Object#send. Eu só não faço isso pois terei que estender este objeto com os métodos de classe, então prefiro usar a classe singleton para manter o código mais legível.

Finalizando

Definir muitos métodos com a classe singleton pode ser prático, mas prejudica a legibilidade e você não quer que isto aconteça. Prefira a classe singleton para métodos que não estão disponíveis no contexto da classe como o attr_accessor. Não é porque você pode fazer algo, que você deve fazê-lo mesmo assim.