2012-07-17 24 views
5

En Ruby, intento crear una clase, que en función de un valor dado durante la inicialización heredará de uno de los siguientes módulos. Me gustaría crear un módulo base del que hereden ambos módulos que contenga métodos comunes que usan constantes definidas en los módulos que lo heredan. Ejemplo:Heredando constantes dentro de un módulo de Ruby

module BaseMod 
    def what_am_i 
    puts OUTPUT 
    end 
end 

module Tall 
    OUTPUT = "I am tall" 
    include BaseMod 
end 

module Short 
    OUTPUT = "I am short" 
    include BaseMod 
end 

class Person 
    def initialize type 
    if type =~ /short/i 
     extend Short 
    else 
     extend Tall 
    end 
    end 
end 

p = Person.new "short" 
p.what_am_i 

Mi problema es que cuando "p.what_am_i" se llama me sale el siguiente error:

NameError: uninitialized constant BaseMod::OUTPUT 
    const_missing at org/jruby/RubyModule.java:2642 
     what_am_i at test_logic2.rb:3 
     (root) at test_logic2.rb:28 

También me pregunto si hay una mejor manera de ir haciendo esto.

Respuesta

2

Para obtener la constante en su situación que tiene que escribir algo como esto:

module Tall 
::OUTPUT = "I am tall" 
include BaseMod 
end 

Sin embargo nota que está redefiniendo el constante con la declaración del módulo corto. Para eso, siempre recibirás "Estoy corto".

Así que para hacerlo correctamente debería probar:

module BaseMod 
OUTPUT="Before" 
def what_am_i 
    puts OUTPUT 
end 
end 

module Tall 
def self.extended(k) 
    OUTPUT.replace "I am tall" 
end 
include BaseMod 
end 

module Short 
def self.extended(k) 
    OUTPUT.replace "I am short" 
end 
include BaseMod 
end 

K

0

Parece que cuando mensaje de su persona P con el mensaje #what_am_i, el intérprete busca la implementación del método cada vez más elevados y más alto en los antecesores de la clase, y finalmente lo encuentra en BaseMod, pero en ese nivel, la constante OUTPUT ya no está definida. Así que creo que Ruby continúa buscando constante de SALIDA yendo hacia arriba en jerarquía, pero no piensa en mirar hacia abajo, en los módulos alto y corto donde está definido. La moral es que, incluso si incluye una gran cantidad de submódulos, no entran en un montón donde todas las constantes son accesibles para todos, sino que mantienen su jerarquía en el orden inverso al de su inclusión (vea Tall.Ancestors). En cualquier nivel, solo se puede acceder a las mismas constantes de nivel o nivel superior. Resolvería su problema de la siguiente manera:

module Personhood 
    def what_am_i; @output end 
end 

class Tall 
    include Personhood 
    def initialize 
     @output = "I am tall" 
    end 
    end 
end 

class Short 
    include Personhood 
    def initialize 
     @output = "I am short" 
    end 
    end 
end 

def Person(type) 
    if type =~ /short/i 
    Short.new 
    else 
    Tall.new 
    end 
end 

pete = Person "short" 
pete.what_am_i 
=> I am short 

Disponía de una constante a favor de variables de instancia. En Ruby, no hay constantes reales de todos modos. Alto y Corto se hacen clases y Persona se hace un método constructor, que devuelve una clase Alta o Corta dependiendo de su entrada. Así es como siento que debería hacerse.

1

Voy a traer una opción más a la mesa. No estoy muy seguro de lo que su complejo, caso en el mundo real es, así que aquí está mi elección:

module BaseMod 
    def what_am_i 
    puts output 
    end 
end 

module Tall 
    include BaseMod 
    def self.extended klass 
    define_method :output do 
     "I am tall" 
    end 
    end 
end 

module Short 
    include BaseMod 
    def self.extended klass 
    define_method :output do 
     "I am short" 
    end 
    end 
end 

class Person 
    def initialize type 
    extend (type =~ /short/i ? Short : Tall) # Because I didn't wanna type all those lines 
    end 
end 

p = Person.new "short" 
p.what_am_i 

Tenga en cuenta que para esta situación, usted podría hacer tan fácilmente esto:

module Tall 
    include BaseMod 
    def output 
    "I am tall" 
    end 
end 

Pero no sé si eso realmente te ayudaría.

+0

interesante forma de hacer eso. –

+0

@BorisStitnicky dependiendo de lo que desea construir, un método puede tener más sentido que una variable de instancia. YMMV :) – Trevoke

4
module BaseMod 
    def what_am_i 
    puts self.class::OUTPUT 
    end 
end 

module Tall 
    OUTPUT = "I am tall" 
    include BaseMod 
end 

module Short 
    OUTPUT = "I am short" 
    include BaseMod 
end 

class Person 
    def initialize(type) 
    if type =~ /short/i 
     self.class.send(:include, Short) 
    else 
     self.class.send(:include, Tall) 
    end 
    end 
end 

p = Person.new "short" 
p.what_am_i 

Editar: El código anterior no funcionan realmente:

p = Person.new "short" 
p.what_am_i 
>> I am short 
p = Person.new "tall" 
p.what_am_i 
>> I am tall 
p = Person.new "short" 
p.what_am_i 
>> I am tall 

Aquí es otro intento:

module BaseMod 
    def self.included(base) 
    base.send(:define_method, :what_am_i) do 
     puts base::OUTPUT 
    end 
    end 
end 

module Tall 
    OUTPUT = "I am tall" 
    include BaseMod 
end 

module Short 
    OUTPUT = "I am short" 
    include BaseMod 
end 

class Person 
    def initialize type 
    if type =~ /short/i 
     extend Short 
    else 
     extend Tall 
    end 
    end 
end 

p = Person.new "short" 
p.what_am_i 
p = Person.new "tall" 
p.what_am_i 
p = Person.new "short" 
p.what_am_i 
+0

Buen truco, gracias. Pero personalmente, como una opción de diseño, todavía evitaría usar constantes en casos como este. –

+0

Pude verlo de cualquier manera. Las constantes son apropiadas desde la perspectiva de los módulos alto y corto. La constante es extraña desde la perspectiva de Person, pero no es allí donde debería vivir el método what_am_i. En cualquier caso, mi código no funciona bien, así que agregué otra solución. –

+0

Ya veo. No lo intenté, así que no me di cuenta de que no funcionó :) –

Cuestiones relacionadas