class ExampleObject
attr_reader :a, :b, :c, :d, :e
def initialize
@a = "?"
@b = "?"
@c = "?"
@d = "?"
@e = "?"
end
end
我需要像上面这样定义一个类,并且所有的实例变量都设置attr_reader
。如果实例变量越来越多,这句话会越来越长,有没有能直接设置所有实例变量都为attr_reader
呢?
简单的说,attr_reader :a, :b, :c, :d, :e
这一句能不能简单的描述?
mattr_accessor Defines both class and instance accessors for class attributes.
mattr_accessor(*syms): Creates a class-variable attr_accessor that can be accessed both on an instance and class level.
你好,你可能误解了我的意思。我只针对这个一个 class,没有涉及到 module。
instance_variables.each do |var|
self.class.class_eval do
attr_reader var[1..-1]
end
end
放到 initialize
方法最后,只针对单个方法中的实例变量,因为实例变量声明的位置不固定。
另外,要这样做之前请确定你已经知道 ocp 原则和代码的用处,产品代码这么玩会被人问候的
class ExampleObject
def initialize
@a = "?"
@b = "?"
@c = "?"
@d = "?"
@e = "?"
end
end
e = ExampleObject.new
2.1.2 :058 > e.a => "?" 2.1.2 :059 > e.b => "?"
class X
def initialize
@a = 120
@b = 200
simple_def
end
private
def simple_def
instance_variables.each do |var|
self.class.class_eval do
attr_reader var[1..-1]
end
end
end
end
x = X.new
p x.a, x.b
self.class.class_eval,漏掉了 class 吧。这个有安全问题么?不会吧。
谢谢你们的回复,很有收获! 这是我设计的一个自动化测试框架里面一个描述页面元素的公共类,其它负责业务的类需要访问这个描述页面的类来描述业务,所以描述页面的类的实例变量都需要能够被直接访问。因为这个类的职责很单一,就是描述页面,所以应该不会有什么潜在威胁。 @saiga 谢谢你的提醒,关于 Ruby 程序设计方面我也是在学习中。
#6 楼 @davidlichao 不好意思,刚才试的时候类被污染了。 我写个 method_missing 的。
class ExampleObject
def initialize
@a = "?"
@b = "?"
@c = "?"
@d = "?"
@e = nil
end
def method_missing(method, *arg)
field = "@#{method}".to_sym
if instance_variables.include?(field)
self.class.class_eval do
attr_reader method
end
return instance_variable_get(field)
end
super
end
end
e = ExampleObject.new
#9 楼 @piecehealth #10 楼 @5swords
接触这种元编程比较少,好酷!
这样我重新定义一个包括method_missing
这个方法的类,如果有很多个这种描述对象的类(按模块分),只要继承就好了。果然 D.R.Y.
真的学习了。
我觉着 attr_reader 或者 attr_accessor 是为了方便你使用变量的。你如果有大量变量,真的没有必要设置 attr_reader, 直接 foo.instance_variable_get("@a')
就行了。
如果仅仅是想在 new 一个 instance 的时候确定有哪些 attributes,我想这样会好一些。
class ExampleObject
def initialize
@a = "?"
@b = "?"
@c = "?"
@d = "?"
(class << self; self; end).send(:attr_reader, *(instance_variables.map{|name| name[1..-1]}))
end
end