混合在彼此依赖的模块中?

时间:2013-06-10 23:11:24

标签: ruby

模块A依赖于模块B,而类C依赖于模块AB。如果我将AB包含到C中,这可行,但我不喜欢A依赖于必须与之混合的另一个模块的事实它的工作原理。

在这种情况下混合模块的“正确”方法是什么? A本身应该B混合吗?如果C也直接使用B该怎么办?

module B
  def g
    12
  end
end

module A
  def f
    2 * g
  end
end

class C
  include A, B

  def h
    3 * f
  end
end

5 个答案:

答案 0 :(得分:2)

您可以覆盖Module#included

module A
  def self.included(base)
    base.class_eval do
      include B
    end
  end
end

完成后,A就在A之后。

答案 1 :(得分:1)

冗余地编写include没有错。如果AC都依赖于B,则BA中都包含C

module B
  def g; 12 end
  def i; 7 end
end

module A
  include B
  def f; 2 * g end
end

class C
  include A, B
  def h; 3 * f * i end
end

如果C不依赖于B,请仅在B中加入A

module B
  def g; 12 end
end

module A
  include B
  def f; 2 * g end
end

class C
  include A
  def h; 3 * f end
end

答案 2 :(得分:0)

  

模块A的方法依赖于模块B中的功能。[...]在这种情况下,在模块中混合的“正确”方法是什么? B本身应该混合吗?如果C也直接使用B怎么办?

你没有多少选择。应根据A,B和C之间的关系选择其中的每一个。

第一个是声明一个模块D,它将定义AB之间的共享功能。通过这种方式,您可以轻松地将DAB混合,而无需担心太多:

module D; ...; end
module A; include D; ...; end
module B; include D; ...; end

我能想到的另一个是将AB转换为类以便使用继承。当具有两个具有强依赖关系的类时,继承是好的。如果A使用的功能足够强,您一定要选择以下功能:

class B; ...; end
class A < B; ...; end
class C < A; ...; end

答案 3 :(得分:0)

因为在你的情况下,你没有具有相同名称的重写方法,无论你混合使用什么方式,实例方法都将按预期一起工作。你只需要确保它们真的混合在一起:

[ A, B ].all? { |m| C.kind_of? m } # must be true

所以,在你的情况下,你是否这样做无关紧要

class C; include A, B; end

或者

class C; include B, A; end

或者

module B; include A end
class C; include B end

或者

module A; include B end
class C; include A end

提醒您注意的一点是,这不起作用:

class C; include A; end # including the module A first
module A; include B end # and trying to remix it after
c = C.new               # does not work
c.h                     # raises NoMethodError
# Modules have to have finished mixing when they are included.

这就是全部!

答案 4 :(得分:0)

如果A依赖于B并且您的模块仅定义实例方法,那么在A中包含B我没有看到任何错误。如果您需要类方法,可能需要考虑http://api.rubyonrails.org/classes/ActiveSupport/Concern.html

module B
  def method_b
    "B instance method"
  end
end

module A
  include B

  def method_a
    "A instance method is using #{method_b}"
  end
end

class Klass
  include A
end

k = Klass.new
puts k.method_a  #=> A instance method is using B instance method
puts k.method_b  #=> B instance method
puts k.class.ancestors.inspect  #=> [Klass, A, B, Object, Kernel, BasicObject]