2012-09-25 120 views
9

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

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之外,有什么方法可以解决这个问题吗?我确实看到了this example,但是我没有办法只为了达到这个目的而将它们修改成Class

回答

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

编辑:请注意,这是设置一个类的实例变量。当模块混入类中时,不能创建实例上的实例变量,因为这些实例尚未创建。你可以,不过,在混入创建初始化方法,例如:

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 
+0

非常好,谢谢 - 我想知道为什么我没有看到这个方法提到任何地方,即使有一百万篇关于它的文章。 –

+0

不幸的是,这看起来不起作用 - 引用'@ example_ivar'然后返回为'nil'。 –

+0

啊,我明白了,谢谢 - 但是如果这个类已经指定了一个'initialize'方法就会引发问题,对吧? –

2

modules提供了钩子,作为Module#included。我建议你查看关于该主题的ruby文档,或使用ActiveSupport::Concern,它提供了一些模块上的帮助程序。

+0

我没有使用'ActiveSupport',但是感谢我指出了正确的方向。 –

+0

不客气。 – ksol

1

也许这是一个小哈克,但您可以使用prepend来获得所需的行为:

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

class A 
    prepend Foo 
end 

下面是从控制台输出:

2.1.1 :011 > A.new 
=> #<A:0x00000101131788 @instance_var=[]> 
+0

使用prepend而不是include有什么缺点? –

+0

我能想到的一个缺点是模块被插入层次结构中类的下面。通常,我们的类型层次结构看起来像'A Max

+0

好吧:) 那么“prepend”对我来说是相当不言自明的,所以我喜欢(和使用)你的解决方案,谢谢! (+1) –

0

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

经过反思,如果包含该模块的类也有一个需要初始化的超类,那么这将无法很好地工作。模块中的初始化器需要接受一个可变参数列表并将其传递给超类。它看起来是一个很好的探索途径。