这是一种常见的初始化模式:
def initialize(title,val,type) @title,@val,@type = title,type end
是否存在等同于“获取每个参数,创建同名属性,并将属性设置为参数值”的快捷方式?
我正在寻找一种无宝石的解决方案.
解决方法
这是你如何做到这一点.将为每个初始化参数创建一个实例变量和一个关联的读/写访问器,变量具有相同的名称,以@开头,并且每个实例变量将被赋予相关参数的值.
码
class MyClass def initialize(< arbitrary parameters >) self.class.params.each { |v| instance_variable_set("@#{v}",instance_eval("#{v}")) } < other code > end @params = instance_method(:initialize).parameters.map(&:last) @params.each { |p| instance_eval("attr_accessor :#{p}") } class << self attr_reader :params end < other code > end
例
class MyClass def initialize(a,b,c) self.class.params.each { |v| instance_variable_set("@#{v}",instance_eval("#{v}")) } end @params = instance_method(:initialize).parameters.map(&:last) @params.each { |p| instance_eval("attr_accessor :#{p}") } class << self attr_reader :params end end MyClass.methods(false) #=> [:params] MyClass.instance_methods(false) #=> [:a,:a=,:b,:b=,:c,:c=] m = MyClass.new(1,2,3) m.a #=> 1 m.b #=> 2 m.c #=> 3 m.a = 4 m.a #=> 4
说明
解析类MyClass时,为类实例变量@params分配一个数组,其元素是initialize的参数.这是可能的,因为在解析开始@params = …的代码时创建了方法初始化.
方法Method#parameters用于获得初始化的参数.对于上面的例子,
instance_method(:initialize).parameters #=> [[:req,:a],[:req,:b],:c]]
所以
@params = instance_method(:initialize).parameters.map(&:last) #=> [:a,:c]
然后我们创建读/写访问器:
@params.each { |p| instance_eval("attr_accessor :#{p}") }
和@params的读访问器,供初始化使用:
class << self attr_reader :params end
创建MyClass的实例my_class时,传递给MyClass.new的参数值将传递给initialize.通过类实例变量@params初始化然后循环并设置每个实例变量的值.在这个例子中,
MyClass.new(1,3)
调用initialize(a,c)where
a => 1 b => 2 c => 3
我们有:
params = self.class.params #=> [:a,:c] params.each { |v| instance_variable_set("@#{v}",instance_eval("#{v}")) }
对于params的第一个元素(:a),这是:
instance_variable_set("@a",instance_eval(a) }
这是:
instance_variable_set("@a",1 }
导致@a被分配1.
请注意@params的访问器不是必需的:
class MyClass def initialize(a,c) self.class.instance_variable_get(:@params).each { |v| instance_variable_set("@#{v}",instance_eval("#{v}")) } end @params = instance_method(:initialize).parameters.map(&:last) @params.each { |p| instance_eval("attr_accessor :#{p}") } end