3

我有一个疱疹的时刻。我可以发誓这行得通:

module StubbedGreeting
  def sayit
    puts "StubbedGreeting"
  end
end

module RegularGreeting
  def sayit
    puts "RegularGreeting"
  end
end

class Greeting
  def sayit
    raise "Gotta catch me!"
  end
end

class GreetingIncludes
  include RegularGreeting
end

begin
  Greeting.send(:include, StubbedGreeting)
  Greeting.new.sayit
rescue Exception
  puts "Exception raised"
end

GreetingIncludes.send(:include, StubbedGreeting)
GreetingIncludes.new.sayit

这里发生的Greeting.new.sayit结果rescue是调用块,忽略 StubbedGreeting 尝试的覆盖。

但是,GreetingIncludes.new.sayit结果是“StubbedGreeting”,也不例外。

所以一个模块可以覆盖另一个模块的方法,但不能覆盖已经直接在类中定义的方法?

我知道如何解决这个问题,我只是觉得很奇怪。

4

1 回答 1

4

当包含一个模块时,它的方法按照方法解析顺序放在类的方法和它的父类之间。因此,在解析要调用哪个实际方法时,ruby 首先按以下顺序搜索该方法。如果找到匹配方法,则中止搜索并使用此方法。

  • 对象的单例类
  • 包含在单例类中的模块从最后包含到第一个包含
  • 对象的类
  • 包含在类中的模块

然后对于每个父类继续进行,直到Class到达该类。

如您所见,模块确实不能覆盖在类本身上定义的方法,因为模块在方法解析顺序中位于实际类的后面。如果你真的需要重写这样的方法,你可以使用alias_methodoralias_method_chain来“重命名”方法。

在即将到来的 Ruby 2.0 中将有一个前置机制,它将在类之前包含模块,这将实现您想要的。但它还没有发布。

于 2012-08-28T07:25:55.113 回答