Ruby动态模块混合

时间:2013-07-03 14:11:56

标签: ruby mixins

假设我有两个模块:

module Test1

  attr_accessor :a, :b

  @a = 0.0
  @b = 0.0

end

module Test2

  attr_accessor :c, :d

  @c = 0.0
  @d = 0.0

end

现在,我想有条件地将这些模块混合到一个类中。这就是我尝试过的:

require './Test1.rb'
require './Test2.rb'

class MyClass

  def initialize(mode)
    if mode == 0
      (class << self; include Test1; end)
    elsif mode == 1
      (class << self; include Test2; end)
    else
      class << self
        include Test1
        include Test2
      end
    end
  end

end

这是我看到的行为:

obj = MyClass.new(0)
obj.a  #=> nil

同样,@a在类中的实例方法中是nil。我觉得我不理解这里重要的事情。我想了解为什么我所做的不起作用,以及实现我所需功能的正确方法是什么。

2 个答案:

答案 0 :(得分:11)

您有此行为,因为您在模块中设置的这些实例变量属于模块本身而不属于MyClass实例。请考虑以下代码:

Test1.instance_variable_get(:@a)
# => 0.0

要解决此问题,您可以使用extend代替include

module Test1

  attr_accessor :a, :b

  def self.extended(object)
    object.a, object.b = 0.0, 0.0
  end

end

module Test2

  attr_accessor :c, :d

  def self.extended(object)
    object.c, object.d = 0.0, 0.0
  end

end

在你的班上:

require './Test1.rb'
require './Test2.rb'

class MyClass

  def initialize(mode)
    if mode == 0
      extend Test1
    elsif mode == 1
      extend Test2
    else
      extend Test1
      extend Test2
    end
  end

end

答案 1 :(得分:2)

我想到了解决这个问题的方法,所以我想我会分享它。我还是想知道是否有人知道一种更好的方法来实现我的目标。

Module Test1
  attr_accessor :a, :b

  def init1
    @a = 0.0
    @b = 0.0
  end
end

class MyClass
  def initialize
    if mode == 0
      (class << self; include Test1; end)
      init1
    elsif mode == 1
      ...
  end
end