我有一个看起来像这样的课程:
class Family_Type1
@people = Array.new(3)
@people[0] = Policeman.new('Peter', 0)
@people[1] = Accountant.new('Paul', 0)
@people[2] = Policeman.new('Mary', 0)
def initialize(*ages)
for i in 0 ... @people.length
@people[i].age = ages[i]
end
end
end
我希望能够在运行时定义一组类似于这一类的类(在启动时定义它们一次),其中数组的大小和分配给每个参数的类型是在运行时从外部规范文件定义的。
我有点使用evals工作,但这真的很难看。有更好的方法吗?
答案 0 :(得分:32)
据我所知,您需要元编程。下面是使用初始化实例变量的初始化方法动态(动态)创建类的代码片段 -
class_name = 'foo'.capitalize
klass = Object.const_set(class_name,Class.new)
names = ['instance1', 'instance2'] # Array of instance vars
klass.class_eval do
attr_accessor *names
define_method(:initialize) do |*values|
names.each_with_index do |name,i|
instance_variable_set("@"+name, values[i])
end
end
# more...
end
希望你能根据自己的要求调整它。
答案 1 :(得分:9)
首先,您的示例代码不适合您的部分原因是您有两个不同的@people
变量 - 一个是实例变量,另一个是< em>类实例变量。
class Example
# we're in the context of the Example class, so
# instance variables used here belong to the actual class object,
# not instances of that class
self.class #=> Class
self == Example #=> true
@iv = "I'm a class instance variable"
def initialize
# within instance methods, we're in the context
# of an _instance_ of the Example class, so
# instance variables used here belong to that instance.
self.class #=> Example
self == Example #=> false
@iv = "I'm an instance variable"
end
def iv
# another instance method uses the context of the instance
@iv #=> "I'm an instance variable"
end
def self.iv
# a class method, uses the context of the class
@iv #=> "I'm a class instance variable"
end
end
如果要在类中创建一次变量以在该类的实例方法中使用,请使用constants
或class variables
。
class Example
# ruby constants start with a capital letter. Ruby prints warnings if you
# try to assign a different object to an already-defined constant
CONSTANT_VARIABLE = "i'm a constant"
# though it's legit to modify the current object
CONSTANT_VARIABLE.capitalize!
CONSTANT_VARIABLE #=> "I'm a constant"
# class variables start with a @@
@@class_variable = "I'm a class variable"
def c_and_c
[ @@class_variable, CONSTANT_VARIABLE ] #=> [ "I'm a class variable", "I'm a constant" ]
end
end
即便如此,在您的代码的上下文中,您可能不希望所有Family_Type1实例都引用相同的警察和会计师吗?或者你呢?
如果我们切换到使用类变量:
class Family_Type1
# since we're initializing @@people one time, that means
# all the Family_Type1 objects will share the same people
@@people = [ Policeman.new('Peter', 0), Accountant.new('Paul', 0), Policeman.new('Mary', 0) ]
def initialize(*ages)
@@people.zip(ages).each { |person, age| person.age = age }
end
# just an accessor method
def [](person_index)
@@people[person_index]
end
end
fam = Family_Type1.new( 12, 13, 14 )
fam[0].age == 12 #=> true
# this can lead to unexpected side-effects
fam2 = Family_Type1.new( 31, 32, 29 )
fam[0].age == 12 #=> false
fam2[0].age == 31 #=> true
fam[0].age == 31 #=> true
运行时初始化可以通过元编程来完成,正如Chirantan所说,但是如果你只是初始化几个类,并且你知道他们的名字是什么,你也可以通过使用从文件中读取的任何内容来完成:< / p>
PARAMS = File.read('params.csv').split("\n").map { |line| line.split(',') }
make_people = proc do |klasses, params|
klasses.zip(params).map { |klass,name| klass.new(name, 0) }
end
class Example0
@@people = make_people([ Fireman, Accountant, Fireman ], PARAMS[0])
end
class Example1
@@people = make_people([ Butcher, Baker, Candlestickmaker ], PARAMS[0])
end
答案 2 :(得分:1)
假设您想在运行时创建每个类型/数组大小不同的类:
如果(在Python中)在执行时定义了一个Ruby类(我认为是),那么你可以这样做:
在函数中定义您的类。让函数接收数组大小和类型作为参数,并在其结果中返回类。这样,您就有了一种类工厂来调用spec文件中的每个定义:)
另一方面,如果您想根据实际数据初始化@params
,请记住,Ruby是一种动态类型语言:只需将构造函数中的@params
重新分配给新数组!