Let `super` call the parent class over an included module

时间:2018-11-27 00:56:01

标签: ruby inheritance superclass

I have class Boy that inherits class Person, and includes module Bipedal. Both Person and Bipedal have versions of #two_legs.

module Bipedal
  def two_legs(name)
    puts "#{name} has exactly two limbs used for walking."
  end
end

class Person
  def two_legs(name)
    puts "#{name} has two human legs."
  end
end

class Boy < Person
  include Bipedal
  attr_accessor :name
  def initialize(name)
    @name = name
  end

  def two_legs
    super(@name)
  end
end

Since the Bipedal module is included in Boy, Bipedal#two_legs takes precedence over the Person#two_legs. When I call super on a Boy instance, the module Bipedal takes precedence over the parent class Person.

johnny = Boy.new('Johnny')
johnny.two_legs
# >> "Johnny has exactly two limbs used for walking."

I want to use one version in one place and the other in another. Bipedal has other stuff, so I can't comment out include Bipedal. Is there some standard way to let Boy#two_legs or super to use the parent class version instead of the module version as follows?

johnny.two_legs
# >> "Johnny has two human legs."

I came up with this:

Boy.superclass.instance_method(:two_legs).bind(self).call(@name)

which works in place of super(@name), but is more complicated than I was expecting.

Again, the question is, is there a standard way to force the parent class to take precedence over the module when calling super?

3 个答案:

答案 0 :(得分:3)

否,没有强制调用super的呼叫以特定顺序移动祖先的标准方法。他们以预定的顺序行走。看看the documentation on calling methods

  

发送消息时,Ruby查找与接收者的消息名称匹配的方法。方法存储在类和模块中,因此方法查找可以遍历这些对象,而不是对象本身。

     

这是接收者的类或模块R的方法查找的顺序:

     
      
  • R的相反模块
  •   
  • 有关R中的匹配方法
  •   
  • R包含的模块以相反的顺序
  •   
     

如果R是具有超类的类,则使用R的超类重复此操作,直到找到方法为止。

     

一旦找到匹配项,方法查找就会停止。

由于您的类Boy直接包含模块Bipedal,并且由于所包含的模块是在超类之前进行搜索的,并且由于一旦找到匹配项就会停止搜索,因此永远不会使用超类Person已选中。

使用refinements时也是如此,因为查找总是在检查超类之前命中包含的模块。

如果将include Bipedal移到Person类中,则它将按您期望的方式工作,因为Boy不直接包含模块,因此它将最终在超类的位置进行搜索找到定义的方法。

也许有其他创造性的方法可以实现此目标,例如您提供的Boy.superclass...示例,但是您询问是否可以在调用super时更改查找行为,并且答案是否定的。文档。

答案 1 :(得分:2)

我认为您无法更改super的工作方式,也不能通过“ super super”跳过祖先,但可以使用其他祖先。如果您只是将Bipedal包含在其中某些方法中,并且想跳过其中的一些方法,则可以执行以下操作:

SemiBipedal = Bipedal.dup
SemiBipedal.remove_method(:two_legs)

class Boy < Person
  include SemiBipedal
  #...
end

当然,johnny.is_a? Bipedal不再是正确的,并且Boy.ancestors在索引1处将具有SemiBipedal而不是Bipedal,但这可能无关紧要。

答案 2 :(得分:2)

一个人可以两次使用方法Method#super_method。作为一种花园式的Ruby方法,我相信它可以作为让Boy#two_legs而不是Person#two_legs来调用Bipedal#two_legs的“标准方式”。

class Boy < Person
  include Bipedal
  attr_accessor :name
  def initialize(name)
    @name = name
  end
  def two_legs
    method(:two_legs).super_method.super_method.call(@name)
  end
end

willie = Boy.new('Willie')
willie.two_legs
Willie has two human legs.

请注意以下事项。

willie.method(:two_legs).super_method.owner
  #=> Bipedal
willie.method(:two_legs).super_method.super_method.owner
  #=> Person