2012-07-17 16 views
5

In Ruby, sto provando a creare una classe, che in base a un valore fornito durante l'inizializzazione erediterà da uno dei seguenti moduli. Vorrei creare un modulo base ereditato da entrambi questi moduli che contengano metodi comuni che utilizzano costanti definite nei moduli che lo ereditano. Esempio:Inerenti costanti all'interno di un modulo 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 

Il mio problema è che quando "p.what_am_i" si chiama ottengo il seguente errore:

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 

sto anche chiedendo se c'è un modo migliore per andare a fare questo.

risposta

2

Per ottenere la costante nella vostra situazione devi scrivere qualcosa di simile:

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

Tuttavia nota che si sta ridefinendo il costante con la dichiarazione del modulo breve. Per questo otterrai sempre "I am short".

Quindi, per farlo correttamente si dovrebbe provare:

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

Sembra che quando si messaggio il vostro persona p con il messaggio #what_am_i, l'interprete cerca l'implementazione del metodo in successione più elevata e più in alto negli antenati della classe, e infine lo trova in BaseMod, ma a quel livello, la costante OUTPUT non è più definita. Quindi penso che Ruby continui a cercare la costante OUTPUT andando a verso l'alto in gerarchia, ma non pensa di guardare in basso, nei moduli Alto e Corto dove è definito. Il morale è che, anche se includi molti sottomoduli, non entrano in un unico heap dove tutte le costanti sono accessibili a tutti, ma invece mantengono la loro gerarchia nell'ordine inverso della loro inclusione (vedi Tall.Ancestors). A qualsiasi livello, sono disponibili solo costanti di livello uguale o superiore. Vorrei risolvere il problema nel seguente modo:

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 

Ho eliminato una costante a favore di variabili di istanza. In Ruby, comunque, non ci sono costanti reali. Tall e Short sono classi create e Person viene creato come metodo di costruzione, che restituisce classe Tall o Short a seconda del suo input. È così che sento che dovrebbe essere fatto.

1

Ho intenzione di portare un'altra opzione al tavolo. Io non sono abbastanza sicuro che cosa il vostro complesso, caso nel mondo reale è, quindi ecco la mia scelta:

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 

prega di notare che per questa situazione, si potrebbe facilmente fare questo:

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

Ma non so se questo ti potrebbe davvero aiutare.

+0

modo interessante di fare quello. –

+0

@BorisStitnicky a seconda di ciò che si desidera creare, un metodo può avere più senso di una variabile di istanza. 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 

Edit: Il codice di cui sopra non funziona in realtà:

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 

Ecco un altro tentativo:

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

Bel trucco, grazie. Ma personalmente, come scelta di design, eviterei comunque di usare costanti in casi come questo. –

+0

Potrei vederlo in entrambi i modi. Le costanti sono appropriate dal punto di vista dei moduli Tall e Short. La costante è strana dal punto di vista di Person, ma non è il caso in cui il metodo what_am_i dovrebbe comunque vivere. In ogni caso, il mio codice non funziona correttamente, quindi ho aggiunto un'altra soluzione. –

+0

Vedo. Non l'ho provato, quindi non ho notato che non ha funzionato :) –

Problemi correlati