在Mixins中初始化实例变量

是否有任何干净的方法来初始化模块中的实例变量以用作Mixin? 例如,我有以下内容:

module Example def on(...) @handlers ||= {} # do something with @handlers end def all(...) @all_handlers ||= [] # do something with @all_handlers end def unhandled(...) @unhandled ||= [] # do something with unhandled end def do_something(..) @handlers ||= {} @unhandled ||= [] @all_handlers ||= [] # potentially do something with any of the 3 above end end 

请注意,我必须一次又一次地检查每个@member是否已在每个函数中正确初始化 – 这有点令人恼火。 我宁愿写:

 module Example def initialize @handlers = {} @unhandled = [] @all_handlers = [] end # or @handlers = {} @unhandled = [] # ... end 

而不必重复确保事情正确初始化。 但是,据我所知,这是不可能的。 除了向Example添加initialize_me方法并从扩展类调用initialize_me之外,还有什么方法吗? 我确实看到了这个例子 ,但是为了实现这一目标,我无法将其修改为Class

 module Example def self.included(base) base.instance_variable_set :@example_ivar, :foo end end 

编辑 :请注意,这是设置类实例变量。 当模块混合到类中时,无法创建实例上的实例变量,因为尚未创建这些实例。 但是,您可以在mixin中创建初始化方法,例如:

 module Example def self.included(base) base.class_exec do def initialize @example_ivar = :foo end end end end 

在调用包含类的初始化方法(任何人?)时,可能有一种方法可以做到这一点。 不确定。 但这是另一种选择:

 class Foo include Example def initialize @foo = :bar after_initialize end end module Example def after_initialize @example_ivar = :foo end end 

modules提供钩子,包括Module#included 。 我建议你查看关于这个主题的ruby doc,或者使用ActiveSupport::Concern ,它提供了一些关于模块的帮助器。

也许这有点hacky,但你可以使用prepend来获得所需的行为:

 module Foo def initialize(*args) @instance_var = [] super end end class A prepend Foo end 

以下是控制台的输出:

 2.1.1 :011 > A.new => # 

我认为可能有一个更简单的答案。 该模块应该有一个初始化程序,可以像往常一样初始化变量。 在包含模块的类的初始化程序中,调用super()以在包含的模块中调用初始化程序。 这只是遵循Ruby中的方法调度规则。

反思,如果包含模块的类也有一个需要初始化的超类,这将不会很好。 模块中的初始化程序需要接受变量参数列表并将其传递给超类。 虽然它看起来是一个很好的途径。