2012-12-12 12 views
8

W Rubim 1.9.3 muszę utworzyć kilka instancji klasy, z których każda ma podobne metody klasy i instancji, ale które różnią się tylko o kilka stałych parametrów. Rozróżnienie ich typu klasy jest również ważne, więc nie mogę po prostu użyć oddzielnych instancji tej samej klasy.Zdefiniowanie metod klas Dynamicznie w Rubim

Uproszczony przykład wygląda następująco.

module Animal 
private 
    def self.make_animal(name, legs, noise) 
    klass = Class.new 
    klass.const_set(:NUM_LEGS, legs) 
    klass.class.send(:define_method, :scream) { noise.upcase + '!' } 
    Animal.const_set(name, klass) 
    end 
    make_animal :Tiger, 4, 'roar' 
    make_animal :Human, 2, 'derp' 
end 

ten wydaje się działać dobrze z tym, że zmienne stosowane w bloku, który dynamicznie wyznacza „Scream” metoda jest związana w czasie działania „Scream” zamiast wykonywania sposobu z „make_animal” metodą.

Animal::Human::NUM_LEGS # => 2 -- ok 
Animal::Tiger::NUM_LEGS # => 4 -- ok 
Animal::Human.scream # => "DERP!" -- ok 
Animal::Tiger.scream # => "DERP!" -- fail! 

Jak mogę zmodyfikować powyższy kod tak, że Tiger krzyczy "ROAR!"?

[Uwaga] I naprawdę trzeba zachować strukturę głupkowaty OO na przykład z powodów, które są zbyt zaangażowani tu opisać. Interesuje mnie tylko nauka programowania programowo metod klas na dynamicznie definiowanych klasach z sparametryzowanymi implementacjami metod.

Odpowiedz

10

klass.class jest taka sama w obu przypadkach (klasa): wszystkie zajęcia są przypadki Class. W rezultacie definiujesz krzyk, a następnie redefiniujesz go.

To, co często uważa się za metodę klasową w rubinie, to w rzeczywistości metody singletonowe (jest mnóstwo rzeczy do przeczytania na temat eigenclasses itp., Jeśli jesteś zainteresowany).

def some_object.foo 
end 

Construct tworzy metody singleton. Bardzo często to będzie wewnątrz definicji klasy, wykorzystując siebie, ale można to zrobić na cokolwiek, na przykład jeśli nie

x = 'dog' 
def x.bark 
    "Woof" 
end 

Wtedy x.bark powrócić Hau, ale kora nie zostaną określone na dowolny inny strunowy.

Tutaj Twoja metoda musi odwoływać się do zmiennej noise, więc aby zdefiniować swoją metodę, musisz użyć define_singleton_method.

Jeśli nadal znajdujesz się w Ruby 1.8, nie możesz użyć metody define_singleton_method - musisz użyć faktu, że metody singleton są metodami na eigenclassie.

klass = Class.new 
eigenclass = class << klass; self; end 
eigenclass.send(:define_method, :scream){noise} 

jest równoważne użyciu define_singleton_method

+5

Ach, 'define_singleton_method'.Zapomniałem o tym :) –

+0

Tak, "define_singleton_method" wydaje się być tym, czego mi brakowało. – maerics

4

Problem z kodem nie jest złym momentem wiążącym. Chodzi o to, że definiujesz metodę na Class#class. I to jest niespodzianka - niespodzianka, Class, jedyna. Nadpisujesz wersję "ryk" wersją "derp".

Zamiast tego należy bezpośrednio definiować metody na tych klasach dynamicznych. Oto moja wersja (używa ona instancji var dla noise, mam nadzieję, że to nie problem).

module Animal 
private 
    def self.make_animal(name, legs, noise) 
    klass = Class.new 
    klass.const_set(:NUM_LEGS, legs) 
    klass.instance_variable_set(:@noise, noise) 

    klass.instance_eval do |k| 
     def scream 
     @noise.upcase + '!' 
     end 
    end 

    Animal.const_set(name, klass) 
    end 

    make_animal :Tiger, 4, 'roar' 
    make_animal :Human, 2, 'derp' 
end 

Animal::Human::NUM_LEGS # => 2 
Animal::Tiger::NUM_LEGS # => 4 

Animal::Human.scream # => "DERP!" 
Animal::Tiger.scream # => "ROAR!" 
+0

Ach tak, dobry połów na '' klass.class ... bit głupie mnie =) – maerics

Powiązane problemy