No post sobre blocos em Ruby, rasguei elogios para a linguagem quanto à versatilidade e simplicidade. Neste post vou pular essa parte (que acredito estar implícita) e falar sobre módulos e mixins em Ruby.
Assim como os blocos, os módulos também estão presentes em toda parte no código de uma aplicação desenvolvida com Ruby on Rails. O framework Rails faz amplo uso dessa facilidade da linguagem Ruby para disponibilizar funcionalidades por todas as camadas da aplicação, o que também pode ser explorado pelo desenvolvedor em funcionalidades próprias.
Para quem vem da linguagem Java, módulos podem se parecer com as interfaces, no entanto, diferem em aspectos fundamentais.
Módulos x Interfaces x Herança Múltipla
A primeira grande diferença é que os módulos em Ruby implementam efetivamente métodos que serão "embutidos" em qualquer classe que os inclua. Outra grande diferença é que os módulos, diferentemente das interfaces, não têm como finalidade desempenhar papel polimórfico no conceito de is-a envolvendo a tipagem dos objetos e a delegação da implementação especializada para as classes.
Este link, da DevMedia, esclarece bastante o conceito de polimorfismo para quem não está tão familiarizado.
Módulos, então, adicionam livremente funcionalidades prontas às classes, sem que a linguagem necessite aceitar herança múltipla, o que também ofereceria esta possibilidade mas distorceria os aspectos de tipagem e polimorfismo.
A nível de curiosidade, Java também não permite herança múltipla, mas também não oferece nativamente recurso similar aos módulos de Ruby, no entanto há um projeto nessa linha chamado Qi4j que merece uma conferida.
Module x Class
A principal diferença entre módulos e classes é que a Class implementa a possibilidade de instanciar (método new), ou seja, uma classe pode ser instanciada, enquanto um módulo não oferece esta possibilidade. Outra diferença, relacionada a esta primeira, é que uma classe pode especificar herança e ser herdada, enquanto módulos não o podem.
As semelhanças são que módulos podem conter:
- métodos de instância (para as instâncias das classes que os incluírem)
- métodos de módulo (como métodos de classe, também conhecidos como métodos estáticos)
- definição de classes e constantes
Tanta teoria e nenhum módulo à vista ainda.
Módulos então têm duas funções em Ruby: namespaces e mixins.
Módulos como namespace
Desempenhando o papel de namespace, módulos incluem somente elementos acessíveis de forma estática, podendo até possuir métodos de instância, porém sem que estes últimos façam sentido em função de seu papel de namespace. Um exemplo na API é o módulo Math que possui por exemplo o método sqrt que calcula a raiz quadrada de um determinado número.
Math.sqrt(1024) retorna o inteiro 32.
Uma implementação própria, inútil na prática mas útil como exemplo, seria:
module FuncoesMatematicas def FuncoesMatematicas.calcular_raiz_quadrada(numero) return Math.sqrt(numero) end end
FuncoesMatematicas.calcular_raiz_quadrada(1024) retorna o inteiro 32.
E um exemplo de namespace que contém classes e constantes acessíveis externamente seria:
module Utilidades VALOR_PADRAO = "valor padrao" class SimuladorDeUniverso ... def simular_planetas ... end ... end end
Para classes e constantes definidas dentro do escopo de um método o acesso é feito com :: diferentemente dos métodos estáticos exemplificados antes.
Utilidades::SimuladorDeUniverso Utilidades::VALOR_PADRAO
Se você achou familiar, está plenamente correto. Em uma aplicação Rails, por exemplo, as classes de modelo por padrão herdam de ActiveRecord::Base, ou seja, herdam da classe Base que está contida em um módulo chamado ActiveRecord.
class Pessoa < ActiveRecord::Base ... end
Módulos como mixins
E aqui entra o recurso mais interessante oferecido pelos módulos. Abaixo um exemplo bem simples:
module InstrumentoAcustico def afinar puts "afinando" end end module InstrumentoEletronico def ligar puts "ligando" end def desligar puts "desligando" end end module InstrumentoDeCordas def trocar_cordas puts "trocando as cordas" end end class Violao include InstrumentoAcustico include InstrumentoDeCordas end class PianoEletrico include InstrumentoEletronico end meu_violao = Violao.new meu_violao.afinar meu_violao.trocar_cordas meu_piano = PianoEletrico.new meu_piano.ligar meu_piano.desligar
Com os módulos e classes definidos, os comandos ao final do código imprimem:
afinando trocando as cordas ligando desligando
Difícil postar uma explicação resumida sobre um recurso que abre tantas possibilidades na linguagem Ruby, por isso para mais exemplos e também detalhes sobre a implementação de módulos, namespaces, mixins, cuidados necessário, inclusão dinâmica de módulos, entre outras coisas, recomendo o capítulo 12 do livro The Book of Ruby e o capítulo 16 do livro Eloquent Ruby.
Até a próxima!
Really Good blog post.provided a helpful information.I hope that you will post more updates like this Ruby on Rails Online Training Bangalore
ResponderExcluir