在Mixins中初始化实例变量

时间:2012-09-25 15:20:25

标签: ruby 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

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

4 个答案:

答案 0 :(得分:14)

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

答案 1 :(得分:2)

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

答案 2 :(得分:1)

也许这有点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=[]>

答案 3 :(得分:0)

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

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