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 levanos à 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 fornecemnos 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 modulelocal 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 incluilos 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( "Tralalalala....")
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