让 `super` 通过包含的模块调用父 class
Let `super` call the parent class over an included module
我有 class Boy
继承 class Person
,并包含模块 Bipedal
。 Person
和 Bipedal
都有 #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
由于 Bipedal
模块包含在 Boy
中,因此 Bipedal#two_legs
优先于 Person#two_legs
。当我在 Boy
实例上调用 super
时,模块 Bipedal
优先于父 class Person
.
johnny = Boy.new('Johnny')
johnny.two_legs
# >> "Johnny has exactly two limbs used for walking."
我想在一个地方使用一个版本,在另一个地方使用另一个。 Bipedal
还有其他东西,没法注释掉include Bipedal
。是否有一些标准方法让 Boy#two_legs
或 super
使用父 class 版本而不是模块版本,如下所示?
johnny.two_legs
# >> "Johnny has two human legs."
我想到了这个:
Boy.superclass.instance_method(:two_legs).bind(self).call(@name)
代替 super(@name)
,但比我预期的要复杂。
同样,问题是,在调用 super
时,是否有一种标准方法可以强制父 class 优先于模块?
不,没有标准方法强制调用 super
以特定顺序遍历祖先。他们按照预定义的顺序走。看看 the documentation on calling methods:
When you send a message, Ruby looks up the method that matches the name of the message for the receiver. Methods are stored in classes and modules so method lookup walks these, not the objects themselves.
Here is the order of method lookup for the receiver's class or module R
:
- The prepended modules of
R
in reverse order
- For a matching method in
R
- The included modules of
R
in reverse order
If R
is a class with a superclass, this is repeated with R
's superclass until a method is found.
Once a match is found method lookup stops.
由于您的 class Boy
直接包含模块 Bipedal
,并且由于包含的模块在超级 class 之前被搜索,而且一旦匹配到,搜索就会停止发现,superclass Person
从未被检查过。
使用 refinements 时也是如此,因为查找总是在检查 superclass.
之前命中包含的模块
如果您将 include Bipedal
移动到 Person
class 中,那么它将按照您期望的方式工作,因为 Boy
不直接包含模块,所以它最终会在 superclass 中搜索定义的方法。
可能还有其他创造性的方法可以实现此目标,例如您提供的 Boy.superclass...
示例,但您询问是否可以在调用 super
时更改查找行为,答案是根据文档没有。
我不认为你可以改变 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
但这可能无关紧要。
一个人可以使用方法 Method#super_method 两次。这是一种普通的 Ruby 方法,我相信它可以作为 "standard way" 让 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
我有 class Boy
继承 class Person
,并包含模块 Bipedal
。 Person
和 Bipedal
都有 #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
由于 Bipedal
模块包含在 Boy
中,因此 Bipedal#two_legs
优先于 Person#two_legs
。当我在 Boy
实例上调用 super
时,模块 Bipedal
优先于父 class Person
.
johnny = Boy.new('Johnny')
johnny.two_legs
# >> "Johnny has exactly two limbs used for walking."
我想在一个地方使用一个版本,在另一个地方使用另一个。 Bipedal
还有其他东西,没法注释掉include Bipedal
。是否有一些标准方法让 Boy#two_legs
或 super
使用父 class 版本而不是模块版本,如下所示?
johnny.two_legs
# >> "Johnny has two human legs."
我想到了这个:
Boy.superclass.instance_method(:two_legs).bind(self).call(@name)
代替 super(@name)
,但比我预期的要复杂。
同样,问题是,在调用 super
时,是否有一种标准方法可以强制父 class 优先于模块?
不,没有标准方法强制调用 super
以特定顺序遍历祖先。他们按照预定义的顺序走。看看 the documentation on calling methods:
When you send a message, Ruby looks up the method that matches the name of the message for the receiver. Methods are stored in classes and modules so method lookup walks these, not the objects themselves.
Here is the order of method lookup for the receiver's class or module
R
:
- The prepended modules of
R
in reverse order- For a matching method in
R
- The included modules of
R
in reverse orderIf
R
is a class with a superclass, this is repeated withR
's superclass until a method is found.Once a match is found method lookup stops.
由于您的 class Boy
直接包含模块 Bipedal
,并且由于包含的模块在超级 class 之前被搜索,而且一旦匹配到,搜索就会停止发现,superclass Person
从未被检查过。
使用 refinements 时也是如此,因为查找总是在检查 superclass.
之前命中包含的模块如果您将 include Bipedal
移动到 Person
class 中,那么它将按照您期望的方式工作,因为 Boy
不直接包含模块,所以它最终会在 superclass 中搜索定义的方法。
可能还有其他创造性的方法可以实现此目标,例如您提供的 Boy.superclass...
示例,但您询问是否可以在调用 super
时更改查找行为,答案是根据文档没有。
我不认为你可以改变 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
但这可能无关紧要。
一个人可以使用方法 Method#super_method 两次。这是一种普通的 Ruby 方法,我相信它可以作为 "standard way" 让 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