Classes são Módulos no Ruby

24 de dezembro de 2009  |  Ruby  | 

Qual é a diferença entre uma classe e um módulo no Ruby? Nenhuma, uma classe é um módulo no Ruby. Não acredita em mim?

Class.is_a?(Module) # => true

Agora que você já confia em mim, posso explicar com um pouco mais de calma.

O que são módulos?

Um módulo pode ser definido de forma grosseira como uma coleção de métodos e constantes. Você encontrará dois tipos de métodos em um módulo: métodos de instância e métodos de módulo. Um método de módulo é aquele que pode ser executado sem a necessidade de que o módulo seja incluído em outro objeto:

module MeuModulo
  def self.meu_metodo_de_modulo
    puts "Sou um método de módulo!"
  end
end

MeuModulo.meu_metodo_de_modulo # => Sou um método de módulo!

Por outro lado, métodos de instância, como o nome diz, precisam ser executados a partir de uma instância de um objeto. Módulos podem ser acoplados dentro de outros objetos, tornando assim estes métodos de instância acessíveis.

module MeuModulo
  def meu_metodo_de_intancia
    puts "Método de instância!"
  end
end

class MinhaClasse
  # Acoplando o MeuModulo
  include MeuModulo
end

# Criando uma instância de MinhaClasse
minha_classe = MinhaClasse.new

# Executando o método de instância que foi criado em MeuModulo
puts minha_classe.meu_metodo_de_intancia # => Método de instância!

O que são classes?

Assim como os módulos, classes também são repositórios de métodos. Então qual é a diferença entre um e o outro?

Podemos ver claramente a diferença entre classes e módulos examinando o objeto Class mais de perto:

Class.superclass # => Module

Class.instance_methods(false)
# => ["superclass", "allocate", "new", "to_yaml"]

Como você pode ver no código acima, Class é uma subclasse de Module, mas com quatro métodos a mais. E é exatamente nestes métodos que encontraremos a resposta para a questão levantada acima.

Para esclarecer ainda mais, vamos imaginar como seria a classe Class implementada em Ruby:

class Class < Module
  # Você já sabe como funciona o método new,
  # já que o utilizamos o tempo todo
  def initialize
  end

  # Devolve a superclasse da classe atual
  def superclass
  end

  # Dá suporte ao método new
  def allocate
  end

  def to_yaml
  end
end

Os três primeiros métodos são responsáveis por permitir que você consiga criar uma instância de um objeto e trabalhar com o conceito de hierarquia de classes. O método to_yaml serve apenas como uma interface que dispara um erro do tipo TypeError caso ele não seja implementado por uma subclasse.

O método superclass é fácil de entender, já que a sua única finalidade é informar qual é a superclasse de um objeto. Se uma superclasse não for definida ele retornará nil.

Integer.superclass # => Numeric
Object.superclass # => nil

O método allocate é responsável por reservar um espaço na memória para a nova instância do objeto que estamos criando. O método devolve esta instância pronta.

E por último, o método initialize (new) tem duas funções. Primeiro ele executa o método allocate, que constrói um novo objeto da classe Class (ou de qualquer outra classe, afinal todas elas são subclasses de Class). Em seguida ele dispara o método initialize deste objeto, passando os argumentos para ele.

Finalizando

Se você chegou até aqui, então já entendeu as diferenças entre um módulo e uma classe em Ruby. Basta pensar que ambos são basicamente a mesma coisa (com estas significantes diferenças) e que quase tudo o que vale para um também vale para o outro.

A principal razão de existir estes dois tipos de objetos tão parecidos e ao mesmo tempo tão diferentes é que desta forma podemos deixar nosso código muito mais expressivo.

Normalmente você usará um módulo quando precisar incluí-lo em outro objeto ou usá-lo como um namespace. E você usará uma classe quando precisar de uma instância de um objeto ou utilizar o sistema de heranças. Cada um tem o seu propósito e você será melhor sucedido se utilizar o recurso certo na hora certa.


Este artigo é o primeiro de muitos com a finalidade de explicar como o Ruby e Rails funcionam por dentro, conforme prometido durante a minha palestra no Rails Summit.


14 Comentários


  1. Carlos, obrigado por esta explicação tão clara e objetiva.
    Forte abraço.

  2. Muito bom.
    Essa série de artigos promete.

  3. Discordo da afirmação de que não há diferença entre classes e módulos em Ruby. Sei que o objetivo era usar uma frase de impacto, mas acaba gerando desinformação: apenas classes podem ser instanciadas, apenas módulos podem extender as habilidades de outros módulos e classes. Esta é, aliás, uma decisão que considero errada na linguagem: se classes não podem oferecer tudo o que módulos podem, Class não devia ser uma subclasse de Module. O resultado é que código que precisa garantir que um certo valor é um módulo muitas vezes toma a forma `x.is_a?(Module) && !x.is_a?(Class)`.

  4. Eu não acho muito legal criar métodos dentro de módulos com self porque isto pode criar uma certa confusão caso você inclua este módulo em outra classe.

    Classes como pacotes ou para agrupar código tipo em Lisp eu acho mais interessante usar com module_function dentro do módulo ou com extends self dentro do módulo.

    module Teste
      module_function
    
      def hohoho
        ...

    Ou

    module Teste
      extends self
    
      def hohoho
        ...
  5. Ops, seria:

    module Natal
      extend self
    
      def feliz
        puts self
      end
    end
    
    puts Natal.feliz
  6. Daniel,

    Não entendi muito bem o seu comentário. Você discorda do que? Eu escrevi um artigo inteiro somente para explicar quais as diferenças entre uma coisa e outra. Não entendo onde está a desinformação aí.

    Quanto a necessidade de utilizar o método is_a?(Module) e is_a?(Class), honestamente não consegui imaginar uma situação em que isso fosse necessário.

  7. Daniel Lopes,

    Acho que isto pode variar muito. Um exemplo bem empregado do uso de métodos de módulo é o módulo Math do Ruby. Neste caso ele serve como um namespace para funções matemáticas.

    Claro que estes métodos também poderiam estar todos dentro de uma classe, como métodos de classe. Mas neste caso estaríamos subutilizando os recursos de uma classe já que nunca precisaríamos utilizar herança e nem instanciar um novo objeto Math.

  8. Daniel Lopes,

    Relendo seu comentário, acho que estou falando de uma coisa diferente. Pelo que entendi você não concorda com o uso de self na declaração de métodos, mas aceita a criação de métodos de módulo através de algo como extend self.

    Pode ser interessante… embora eu não tenha problema nenhum com o uso de self.

    p.s.: Desculpe se eu não entendi o ponto.

  9. Eu não gosto de usar o self dentro do módulo para métodos pois se vc incluir este módulo em uma classe pode gerar confusão pois o self no momento da inclusão vai ser alterado.

    Para criar algo como módulo Math eu prefiro o module_function (mas em certas sjtuacoes isto pode gerar um erro como em heranças, aí uso o extend self).

  10. Boa noite,
    Muito boa a explicação mas existem outras diferenças entre módulos e classes??

    E a aplicação de mixis no ruby utilizando módulos aonde isso poderia ser diferente de usar herença ?

    desculpe as perguntas de iniciante.

    Novamente muito bom o post e parabens pelo ótimo trabalho.

    Boa noite

  11. Tordek,

    Sobre mixin isto será tema de um outro post…

  12. Carlos, a parte de desinformação é logo o primeiro parágrafo: “Qual é a diferença entre uma classe e um módulo no Ruby? Nenhuma”. Afirmar que classes são (hierarquicamente) módulos não faz as diferenças desaparecerem.

    Quanto ao código referente a testes com is_a?, de fato, trabalhar com objetos Module não é algo comum (não apenas ser específico com módulos, mas métodos que operam com Module/Class em geral, fora operações básicas como instance_eval). Isso é mais visto no código de implementações da biblioteca padrão, como em JRuby e Rubinius. Essa questão do comportamento incomum de Class, Module e das subclasses dos dois foi motivo de alguns bugs meses atrás no Rubinius ao tentar rodar o RSpec, que cria classes derivadas de Module.

  13. Aqui um exemplo de spec onde módulos (e subclasses) são aceitos, mas classes não:

    http://github.com/rubyspec/rubyspec/blob/master/core/module/include_spec.rb#L38

    Esse tipo de subclasse é uma violação grave do princípio de substituição de Liskov…

  14. Muito bom, Carlos. É um ponto simples, mas muito importante. A explicação está clara e objetiva. Parabéns.

Deixe um comentário