2015-11-01 2 views
3

Я пытаюсь динамически создавать набор классов следующим образом. Ruby: Динамически создавать новые классы

class Foo 
    attr_reader :description 
end 

['Alpha', 'Beta', 'Gamma'].each do |i| 
    klass = Class.new(Foo) do |i| 
    def initialize 
     @description = i 
    end 
    end 
    Object.const_set(i, klass) 
end 

вместо создания каждого класса вручную, е. g .:

class Alpha < Foo 
    def initialize 
    @description = 'Alpha' 
    end 
end 

Каков правильный способ выполнения такой операции и как передать итератор вложенный блок?

ответ

1

Как передать итератор вложенный блок?

Используя вложенный блок . Def не является блоком. Def отключает видимость переменных вне def. блок с другой стороны, может видеть переменные вне блока:

class Foo 
    attr_reader :description 
end 

['Alpha', 'Beta', 'Gamma'].each do |class_name| 
    klass = Class.new(Foo) do 
    define_method(:initialize) do 
     @description = class_name 
    end 
    end 

    Object.const_set(class_name, klass) 
end 

a = Alpha.new 
p a.description 

--output:-- 
"Alpha" 

Вы также можете сделать то, что вы хотите без того, чтобы создать вложенный блок или класс Foo:

['Alpha', 'Beta', 'Gamma'].each do |class_name| 
    klass = Class.new() do 
    def initialize 
     @description = self.class.name 
    end 

    attr_reader :description 

    end 

    Object.const_set(class_name, klass) 
end 

--output:-- 
"Alpha" 
"Gamma" 
+0

'@description = self.class.name' работает с родительским классом или без него. Это аккуратное решение! – mwp

1

Вы близко. Я думаю, вы хотите сделать description переменной экземпляра класса (или, возможно, переменной класса), а не переменной экземпляра. description будет «Alpha» для всех объектов класса Alpha, поэтому он должен быть атрибутом класса. Вы бы получили к нему доступ как Alpha.description (или Alpha.new.class.description). Вот решение с помощью переменной экземпляра класса:

class Foo 
    class << self 
    attr_reader :description 
    end 
end 

['Alpha', 'Beta', 'Gamma'].each do |i| 
    klass = Class.new(Foo) 
    klass.instance_variable_set(:@description, i) 

    Object.const_set(i, klass) 
end 
+0

большой ответ +1 – illusionist

+0

* Описание будет «Alpha» для всех объектов класса Alpha, поэтому оно должно быть атрибутом класса. * - Не обязательно ... потому что изменения в переменной экземпляра * класса * будут влиять на то, что все экземпляры видят , С другой стороны, если переменная создается внутри 'initialize()', то каждый экземпляр будет иметь свою собственную переменную и может быть изменен независимо от других экземпляров. – 7stud

+0

Верно, но мы указываем описание имени класса, поэтому, если это утверждение, которое OP хочет обеспечить, оно будет одинаковым для всех экземпляров класса. Если OP хочет гибкости, чтобы позднее описать описание экземпляра, то да, ваш подход работает лучше. – mwp

1
class Foo 
    attr_reader :description 
end 

['Alpha', 'Beta', 'Gamma'].each do |class_name| 
    eval %Q{ 
    class #{class_name} < Foo 
     def initialize 
     @description = #{class_name} 
     end 
    end 
    } 
end 

Об исполнении:

Gamma.new.description 
=> Gamma