如果mixin方法名称冲突,如何选择要调用的方法?

时间:2012-02-01 19:35:08

标签: ruby

当您在具有方法名称冲突的类中包含模块时,它将使用该类定义的方法。有没有办法选择我想要运行哪一个?

module B
  def self.hello
    "hello B"
  end
end

class A
  include B
  def self.hello
     "hello A"
  end
end

A.hello #=> this prints "hello A", what if I want "hello B"?

3 个答案:

答案 0 :(得分:12)

Ben,当你在Ruby中调用一个方法(比如它是hello)时,会发生这种情况:

  1. 如果接收者的本征类有一个名为hello的方法,它将被调用。如果不是:
  2. 如果接收者的类具有名为hello的实例方法,则会调用它。如果不是:
  3. 如果接收者类包含的任何模块都有一个名为hello的实例方法,则会调用它。如果有多个,最近包含的模块将“赢”。否则:
  4. 如果接收者类的超类有一个名为hello的实例方法,它将被调用。如果不是:
  5. 如果接收者类的超类包含任何模块......
  6. (等等超类的超类,一直到BasicObject ......)
  7. 如果没有找到名为hello的方法,则使用method_missing重复相同的过程,从本征类开始,然后是类,然后包含模块,然后是超类,然后是超类包含的模块...此搜索始终成功,因为Kernel定义了method_missing的默认实现。
  8. 因此,要回答您的问题,如果有多个名为hello的方法,则无法选择将调用哪个方法。上面描述的方法查找规则决定调用哪一个。

    但是:Ruby是一种非常灵活的语言,如果你发布关于你想做什么和为什么做的更多细节,我可能会帮你想到一种模拟所需效果的方法。 / p>

    另一点:如果要将类方法从模块添加到类中,可以执行以下操作:

    module B; def hello; "hello B"; end end
    A.extend(B)
    A.hello
    => "hello B"
    
    你知道吗?当类include是模块时,模块的实例方法成为类的实例方法。当类extend是一个模块时,模块的实例方法成为类的类方法。

答案 1 :(得分:1)

它的设置方式无论如何都不会调用包含模块的方法。尝试在A中省略hello方法,看看调用A.hello时会发生什么。

这将包括来自B的方法。可能有一种更简洁的方法来做到这一点。我只是从我的代码库中删除了它:

module B
  def self.included(base)
    base.extend Greetings
  end

  module Greetings
    def hello
      "hello B"
    end
  end
end

模块的方法将始终覆盖包含模块的方法。这就是它的工作方式。但是,您可以有条件地返回A的hello值,如下所示:

module A
  include B

  def self.hello
    if show_hello_a?
      "hello A"
    else
      super
    end
  end

 def self.show_hello_a?
   false # insert an actual condition statement here
 end

end

答案 2 :(得分:1)

当您致电A.Hello时,所有发生的事情都是将“hello”传递给A对象。然后,A对象必须确定如何处理该消息。它首先会查看它自己的方法,以确定它是否有一个名为“hello”的方法,然后再查看它的父级并包含“hello”方法的模块。

虽然你可以在技术上使用A.ancestors来看B是A的祖先,并且调用它的hello方法,但这会违反A作为对象的抽象。

允许调用这两个方法的正确方法是在A中创建另一个调用B.hello的方法,或者将A.hello命名为其他方法,以便它不会覆盖B.hello的功能。 / p>

编辑:因为你在A中包含了B,所以创建一个在A中调用B的hello的方法就像添加一个调用B.hello的方法一样简单

def self.hello2
  B.hello
end