Cap. 9 – Módulos e Mixins

Anterior Índice Próximo

Módulos e Mixins…

Como mencionado em um capítulo anterior, cada classe Ruby têm somente um 'pai' imediato, embora cada classe pai possa ter vários 'filhos'. Restringindo a hierarquia de classes em um única linha de descendência, o Ruby evita alguns dos problemas que podem ocorrer nas linguagens ( como C++ ) que permitem múltiplas linhas de descendência. Quando classes têm muitos pais assim como muitos filhos e seus pais, e filhos, também podem ter muitos pais e filhos, você corre o risco de terminar em uma rede impenetrável em vez de uma hierarquia limpa e bem ordenada que é o desejável. Não obstante, existem ocasiões que é útil para uma classe poder implementar características de mais de uma outra classe pré­existente. Por exemplo, uma Espada (Sword) pode ser um tipo de Arma (Weapon) mas também um tipo de Tesouro (Treasure); uma Casa pode ser um tipo de Edifício mas também um tipo de Investimento e assim por diante.

Um Módulo é Como Uma Classe…

A solução do Ruby para este problema é fornecida pelos 'Modules' (módulos). À primeira vista, um módulo parece muito com uma classe. Igual a uma classe ele pode conter constantes, métodos e classes. Aqui está um módulo simples: module MyModule GOODMOOD = "happy" BADMOOD = "grumpy" def greet return "I'm #{GOODMOOD}. How are you?" end end Como você vê, este módulo possui uma constante, GOODMOOD e um 'método de instância', greet. Pág. 77 Pequeno Livro do Ruby Cap. 9 – Módulos e Mixins Para tornar isto em uma classe você só precisaria substituir a palavra module, na sua definição, pela palavra classe.

Métodos de Módulo

Em adição aos métodos de instância um módulo também pode ter métodos de módulo que são precedidos pelo nome do módulo: def MyModule.greet return "I'm #{BADMOOD}. How are you?" end Apesar de suas similaridades, existem duas características principais que as classes possuem mas os módulos não: instâncias e herança. Classes podem ter instâncias (objetos), superclasses (pais) e subclasses (filhos); módulos não podem ter nada disso. O que leva­nos à próxima questão: se você não pode criar um objeto a partir de um módulo, para que servem os módulos? Esta é outra questão que pode ser respondida em duas palavras: namespaces e mixins. Os mixins do Ruby fornecem­nos uma forma para lidar com o “pequeno” problema da herança múltipla que eu mencionei anteriormente. Nós voltaremos aos ‘mixins’ em breve. Antes disso, vamos dar uma olhada nos namespaces.

Módulos como Namespaces

Você pode pensar em um módulo como um certo “empacotador” para um conjunto de métodos, constantes e classes. Os vários bits de código dentro do módulo compartilham o mesmo “espaço de nomes” ­ ‘namespace’ ­ que significa que eles são todos visíveis para cada um deles mas não são visíveis para o código exterior ao módulo. A biblioteca de classes do Ruby define vários módulos tais como Math e Kernel. O módulo Math contém métodos matemáticos como sqrt para retornar a raiz quadrada e constantes como PI. O módulo Kernel contém muitos dos métodos que nós temos usado desde o início como print, puts e gets. Constantes Constantes são como var iáveis exceto que seus valor es não mudam (ou não dever iam mudar ). De fato, é (bizar r amente!) possível mudar o valor de uma constante no Ruby mas isto não é nada r ecomendável e o Ruby ir á avisá­lo se você o fizer . Obser ve que as constantes começam com uma letr a maiúscula. Pág. 78 Pequeno Livro do Ruby Cap. 9 – Módulos e Mixins modules1.r b # Ruby Sample program from www.sapphiresteel.com / www.bitwisemag.com module MyModule GOODMOOD = "happy" BADMOOD = "grumpy"

def greet return "I'm #{GOODMOOD}. How are you?" end def MyModule.greet return "I'm #{BADMOOD}. How are you?" end end puts(" MyModule::GOODMOOD") puts(MyModule::GOODMOOD) puts( " MyModule.greet" ) puts( MyModule.greet ) Vamos assumir que nós temos este módulo: module MyModule GOODMOOD = "happy" BADMOOD = "grumpy" def greet return "I'm #{GOODMOOD}. How are you?" end def MyModule.greet return "I'm #{BADMOOD}. How are you?" end end Nós podemos acessar as constantes usando os sinais :: assim: Pág. 79 Pequeno Livro do Ruby Cap. 9 – Módulos e Mixins puts(MyModule::GOODMOOD) Nós podemos, similarmente, acessar métodos do módulo usando a notação de ponto – isto é, especificando o nome do módulo seguido do ponto “.” e do nome do método. O seguinte deveria imprimir “I'm grumpy. How are you?” (Eu estou irritado. Como você está?): puts( MyModule.greet )

“ Métodos de Instância” de Módulos

Isto nos deixa com o problema de como acessar o método de instância, greet. Como os módulos definem um espaço de nomes fechado, o código externo ao módulo não poderá “ver” o método greet, então isto não funciona: puts( greet ) Se esta fosse uma classe em vez de um módulo, é claro, criaríamos objetos da classe usando o método new – e cada objeto separado ( cada “instância” da classe ), poderia ter acesso aos métodos de instância. Mas, como eu disse antes, você não pode criar instâncias de módulos. Então como nós podemos usar os seus métodos de instância? Aí é que aqueles misteriosos mixins entram em cena… modules2.r b

# Ruby Sample program from www.sapphiresteel.com / www.bitwisemag.com module MyModule GOODMOOD = "happy" BADMOOD = "grumpy"

def greet return "I'm #{GOODMOOD}. How are you?" end def MyModule.greet return "I'm #{BADMOOD}. How are you?" end end Pág. 80 Pequeno Livro do Ruby Cap. 9 – Módulos e Mixins include MyModule puts( greet ) puts( MyModule.greet )

Módulos inclusos ou ‘ Mixins’

Um objeto pode acessar os métodos de instância de um módulo bastando incluir o módulo usando o método include. Se você for incluir MyModule em seu programa, tudo no interior do módulo será colocado dentro do escopo corrente. Assim o método greet de MyModule será agora acessível: include MyModule puts( greet ) O processo de incluir um módulo em uma classe é também chamado de ‘misturar’ o módulo – o que explica porque os módulos incluídos são freqüentemente chamados de ‘mixins’. Quando você inclui objetos em uma classe, quaisquer objetos criados a partir da classe poderão usar os métodos de instância do módulo incluso como se tivessem sido definidos na própria classe. modules3.r b # Ruby Sample program from www.sapphiresteel.com / www.bitwisemag.com module MyModule GOODMOOD = "happy" BADMOOD = "grumpy"

def greet return "I'm #{GOODMOOD}. How are you?" end def MyModule.greet return "I'm #{BADMOOD}. How are you?" end end class MyClass include MyModule Pág. 81 Pequeno Livro do Ruby Cap. 9 – Módulos e Mixins def sayHi puts( greet ) end def sayHiAgain puts( MyModule.greet ) end end ob = MyClass.new ob.sayHi ob.sayHiAgain puts(ob.greet) Não somente os métodos desta classe acessam o método greet de MyModule, mas também quaisquer objetos filhos da classe podem acessá­lo, como em: ob = MyClass.new ob.sayHi ob.sayHiAgain puts(ob.greet) Em suma, então, os módulos podem ser usados com um meio de agrupar, de juntar, métodos, constantes e classes relacionadas dentro de um mesmo escopo. Assim, podemos ver os módulos como unidades discretas de código que pode simplificar a criação de bibliotecas de código reusável. modules4.r b # Ruby Sample program from www.sapphiresteel.com / www.bitwisemag.com module MagicThing def m_power return @power end def m_power=(aPower) @m_power=aPower end end Pág. 82 Pequeno Livro do Ruby Cap. 9 – Módulos e Mixins module Treasure attr_accessor :value attr_accessor :insurance_cost end class Weapon attr_accessor :deadliness attr_accessor :power end class Sword < Weapon include Treasure include MagicThing

attr_accessor :name end s = Sword.new s.name = "Excalibur" s.deadliness = 10 s.power = 20 s.m_power = "Glows when Orcs Appear" puts(s.name) puts(s.deadliness) puts(s.power) puts(s.m_power) Por outro lado, você pode estar mais interessado em usar módulos como uma alternativa para herança múltipla. Retornando ao exemplo que eu mencionei no início do capítulo, vamos assumir que você tem uma classe Sword (espada) que não é somente um tipo de Weapon (arma) mas também um tipo de Treasure (tesouro). Pode ser Sword uma descendente da classe Weapon (logo ela herda seus métodos tais como letalidade e potência), mas ela também dever ter métodos da classe Treasure (tais como valor e custo de seguro). Se você define estes métodos dentro de um módulo Treasure em vez de uma classe Treasure, a classe Sword poderia incluir o módulo Treasure para adicionar (misturar ­ ‘mix in’) os métodos de Treasure aos próprios métodos da classe Sword. Note que quaisquer variáveis que são locais para o módulo não podem ser acessadas de fora do módulo. Este é o caso mesmo se um método dentro do módulo tentasse acessar uma variável local e este método fosse chamado pelo código de fora do módulo – por exemplo, quando o módulo é misturado através de inclusão. O programa mod_vars.rb ilustra isso. Pág. 83 Pequeno Livro do Ruby Cap. 9 – Módulos e Mixins mod_var s.r b # Ruby Sample program from www.sapphiresteel.com / www.bitwisemag.com # local variables declared inside a module are not # accessible outside the module ­ even when the module # is mixed in. x = 1 # local to this program module Foo x = 50 # local to module Foo # This can be mixed in but the variable x won't then be visible def no_bar return x end def bar @x = 1000 # You can mix in methods with instance variables, however! return @x end puts( "In Foo: x = #{x}" ) # this can access its local x end include Foo puts(x) # puts( no_bar ) # This can't access the module­local variable x needed by # the no_bar method puts(bar)

Incluindo Módulos de Arquivos

Até agora, nós temos misturados módulos que foram todos definidos dentro de um único arquivo fonte. Freqüentemente é mais útil definir os módulos em arquivos separados e inclui­los quando necessário. A primeira coisa que você tem que fazer para usar o código de outro arquivo é carregar Pág. 84 Pequeno Livro do Ruby Cap. 9 – Módulos e Mixins o arquivo usando o método require, desta forma: r equir emodule.r b # Ruby Sample program from www.sapphiresteel.com / www.bitwisemag.com require( "testmod.rb") puts( " MyModule.greet" ) puts( MyModule.greet ) puts(" MyModule::GOODMOOD") puts(MyModule::GOODMOOD) # puts( greet ) #=> This won't work! include MyModule #mix in MyModule puts( " greet" ) puts( greet ) #=> But now this does work! puts( " sayHi" ) puts( sayHi ) puts( " sayHiAgain" ) puts( sayHiAgain ) sing puts(12346) testmod.r b # Ruby Sample program from www.sapphiresteel.com / www.bitwisemag.com module MyModule GOODMOOD = "happy" BADMOOD = "grumpy"

def greet return "I'm #{GOODMOOD}. How are you?" end def MyModule.greet return "I'm #{BADMOOD}. How are you?" end def sayHi Pág. 85 Pequeno Livro do Ruby Cap. 9 – Módulos e Mixins return greet end def sayHiAgain return MyModule.greet end end def sing puts( "Tra­la­la­la­la….") end puts( "module loaded") sing O arquivo requerido deve estar no diretório corrente, no caminho de procura do sistema operacional (path) ou em uma pasta listada na variável de array predefinida $:. Você pode adicionar um diretório a este array usando o método << desta forma: $: << "C:/mydir" O método require retorna true se o arquivo especificado é carregado com sucesso; senão ele retorna false. Em caso de dúvida, você pode simplesmente mostrar o resultado: puts(require( "testmod.rb" )) Módulo Predefinidos Os seguintes módulos estão embutidos no interpretador do Ruby: Comparable, Enumerable, FileTest, GC, Kernel, Math, ObjectSpace, Precision, Process, Signal O mais importante dos módulos predefinidos é o Kernel que, como já mencionei, fornece muitos dos métodos padrão do Ruby tais como gets, puts, print e require. Em comum com muitas das classes do Ruby, o módulo Kernel é escrito na linguagem C. Mesmo o Kernel sendo, de fato, ‘embutido’ no interpretador Ruby, conceitualmente ele pode ser considerado como um módulo misturado que, como um mixin normal do Ruby, torna seus métodos diretamente disponíveis para quaisquer classes que os requeiram; já que ele é misturado na classe Object, da qual todas as outras classes do Ruby descendem, os métodos do módulo Kernel são universalmente acessíveis.

Anterior Índice Próximo

-- LeandroNunes - 18 Oct 2006
Topic revision: r1 - 05 Jul 2008, UnknownUser
This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Wiki-Colivre? Send feedback