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
?
答案 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