访问模块中定义的类变量

时间:2016-05-17 15:08:47

标签: ruby oop rspec

因此,虽然我反对以这种方式扩展现有的类,但有时(黑客rspec)有必要做类似的事情:

module MyModule
  module ClassMethods
    def define_something(name)
      @@names ||= []
      @@names << name
    end
  end
  def self.included(base)
    base.extend ClassMethods
  end
  def all_names
    @@names
  end
end
class Example
  include MyModule
  define_something "one"
  define_something "two"
end
Example.new.all_names

然后它会产生这个错误:

NameError: uninitialized class variable @@names in MyModule

我理解,因为在撰写MyModule::ClassMethods时 - 我们正在处理实例而不是类(不是self.),所以我尝试了:

module MyModule
  module ClassMethods
    def define_something(name)
      @names ||= []
      @names << name
    end
  end
  def self.included(base)
    base.extend ClassMethods
  end
  def all_names
    @@names
  end
end
class Example
  include MyModule
  define_something "one"
  define_something "two"
end
Example.new.all_names

它也不起作用,最后我最终得到了:

module MyModule
  module ClassMethods
    def define_something(name)
      @names << name
    end
  end
  def self.included(base)
    base.instance_variable_set(:@names, [])
    base.send(:define_method, :all_names) { base.instance_variable_get(:@names) }
    base.extend ClassMethods
  end
end
class Example
  include MyModule
  define_something "one"
  define_something "two"
end
Example.new.all_names

有更好的方法吗?

2 个答案:

答案 0 :(得分:4)

从设计角度来看,通常使用实例变量引用来避免跨越类/实例行。这通常更清楚:

module MyModule
  module ClassMethods
    def define_something(name)
      self.defined_somethings << name
    end

    def defined_somethings
      @_my_module_names ||= []
    end
  end

  def self.included(base)
    base.extend ClassMethods
  end

  def all_names
    self.class.defined_somethings
  end
end

class Example
  include MyModule
  define_something "one"
  define_something "two"
end

Example.new.all_names.inspect
#=> ["one","two"]

我在这里注意创建一个具有详细名称的类级实例变量。调用它@name可能会使它与包含此模块的类定义的变量冲突。请记住,在设计mixin代码时,你是一位客人,你需要更加礼貌。

@@name这样的类型实例变量有时会出现问题,因为它们最终会根据它们的使用方式跨越继承链。定义方法意味着您可以在链中的任何位置覆盖它,这是共享变量无法实现的。

换句话说,将实例的类视为一个单独的对象,并进行明确的,明确定义的方法调用以保持这种分离。

答案 1 :(得分:1)

尝试以下

module MyModule
  module ClassMethods
    def define_something(name)
      @names ||= []
      @names << name
    end
  end
  def self.included(base)
    base.extend ClassMethods
  end
  def all_names
    self.class.instance_variable_get(:@names)
  end
end
class Example
  include MyModule
  define_something "one"
  define_something "two"
end

Example.new.all_names

可以使用@@运算符从类级方法(即self.方法)访问类变量。

@ tadman的答案有类似问题的通用方法(通常在提供模块来实现功能的宝石中找到)