11

是否有任何干净的方法来初始化要用作 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

并且不必反复确保正确初始化事物。但是,据我所知,这是不可能的。除了向扩展类添加initialize_me方法Example和调用之外,还有什么办法可以解决这个问题?initialize_me我确实看到了这个例子,但是我不可能Class为了完成这个而将猴子修补到里面。

4

4 回答 4

13
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
于 2012-09-25T15:24:34.393 回答
2

也许这有点 hacky,但您可以使用它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=[]>
于 2014-04-07T04:41:37.497 回答
1

modules提供挂钩,如Module#included. 我建议您查看有关该主题的 ruby​​ 文档,或使用ActiveSupport::Concern,它提供了一些有关模块的帮助。

于 2012-09-25T15:26:01.770 回答
0

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

仔细想想,如果包含模块的类也有一个需要初始化的超类,这将不会很好地工作。模块中的初始化程序需要接受可变参数列表并将其传递给超类。不过,这似乎是一个很好的探索途径。

于 2014-12-02T10:31:11.490 回答