Ruby 用 attr_reader 添加所有实例变量的方法?

davidlichao · May 27, 2014 · Last by serco replied at May 29, 2014 · 3440 hits
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.

#1 楼 @liwei78

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 原则和代码的用处,产品代码这么玩会被人问候的

#2 楼 @davidlichao

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 => "?"

好象是个假问题,如果有问题请参照 #3 楼 @saiga

#3 楼 @saiga

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 吧。这个有安全问题么?不会吧。

#3 楼 @saiga #4 楼 @5swords

谢谢你们的回复,很有收获! 这是我设计的一个自动化测试框架里面一个描述页面元素的公共类,其它负责业务的类需要访问这个描述页面的类来描述业务,所以描述页面的类的实例变量都需要能够被直接访问。因为这个类的职责很单一,就是描述页面,所以应该不会有什么潜在威胁。 @saiga 谢谢你的提醒😃,关于 Ruby 程序设计方面我也是在学习中。

#5 楼 @chenge 一开始是漏了 class 了.. 这样做没有安全问题,但有设计问题。ruby 默认遵循开放封闭原则,对象属性应该最小开放。

#7 楼 @saiga 是的,减少耦合。 应该手工维护,如果变量过多可能要考虑是否单一职责了。

用 method_missing 也可以

#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. 真的学习了。 😃

#12 楼 @5swords 哈哈好巧,我昨天正好在亚马逊买了你推荐的第二本。谢谢!

我觉着 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
You need to Sign in before reply, if you don't have an account, please Sign up first.