在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中的方法调度规则。
反思,如果包含模块的类也有一个需要初始化的超类,这将不会很好。 模块中的初始化程序需要接受变量参数列表并将其传递给超类。 虽然它看起来是一个很好的途径。