Initializing instance variables in Mixins

旧城冷巷雨未停 提交于 2019-12-03 12:15:53

问题


Is there any clean way to initialize instance variables in a Module intended to be used as Mixin? For example, I have the following:

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

Notice that I have to check again and again if each @member has been properly initialized in each function -- this is mildly irritating. I would much rather write:

module Example

  def initialize
    @handlers     = {}
    @unhandled    = []
    @all_handlers = []
  end

  # or
  @handlers  = {}
  @unhandled = []
  # ...
end

And not have to repeatedly make sure things are initialized correctly. However, from what I can tell this is not possible. Is there any way around this, besides adding a initialize_me method to Example and calling initialize_me from the extended Class? I did see this example, but there's no way I'm monkey-patching things into Class just to accomplish this.


回答1:


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

Edit: Note that this is setting a class instance variable. Instance variables on the instance can't be created when the module is mixed into the class, since those instances haven't been created yet. You can, though, create an initialize method in the mixin, e.g.:

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

There may be a way to do this while calling the including class's initialize method (anybody?). Not sure. But here's an alternative:

class Foo
  include Example

  def initialize
    @foo = :bar
    after_initialize
  end
end

module Example
  def after_initialize
    @example_ivar = :foo
  end
end



回答2:


Perhaps this is a little hacky, but you can use prepend to get the desired behavior:

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

class A
  prepend Foo
end

Here is the output from the console:

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



回答3:


modules provides hooks, as Module#included. I suggest you check out ruby doc on the topic, or use ActiveSupport::Concern, which provides some helpers on modules.




回答4:


I think there may be a simpler answer to this. The module should have an initializer that initialises the variables as you normally would do. In the initializer for the class that includes the module, invoke super() to invoke the initializer in the included module. This is simply following the method dispatch rules in Ruby.

On reflection, this will not work so well if the class including the module also has a superclass that needs to be initialised. The initializer in the module would need to accept a variable parameter list and pass this up to the superclass. It looks like a good avenue to explore though.



来源:https://stackoverflow.com/questions/12586051/initializing-instance-variables-in-mixins

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!