因此,虽然我反对以这种方式扩展现有的类,但有时(黑客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
有更好的方法吗?
答案 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的答案有类似问题的通用方法(通常在提供模块来实现功能的宝石中找到)