Ruby:两个具有相同名称的方法之间的关系,一个对于另一个是“超级”

时间:2012-10-17 19:04:20

标签: ruby

在下面的茶计时器代码中,SleepTimer中有一个'start'方法,它调用'notify'。

  def start
    sleep minutes * 60
    notifier.notify("Tea is ready!")
  end

如果查看下面的代码,您会看到class StdioUi中的通知方法以及module UiWithBeep中的通知方法。上面显示的start方法调用module UiWithBeep中的notify方法,然后通过'super'调用class StdioUi中的notify方法。 (效果是在“Tea ready”之前听到“BEEP!”。)但是,我不明白为什么notifier.notify在module UiWithBeep而不是class StdioUi中调用notify方法。 第一个问题:如何知道转到另一个'通知'。 SecondQuestion 而且,虽然我理解super,但是建立关系的是什么,以便class StdioUi中的通知对另一个通知是'超级'。你能解释一下吗

茶计时器

class TeaClock
  attr_accessor :timer
  attr_accessor :ui

  def initialize(minutes)

    self.ui = StdioUi.new

    self.timer = SleepTimer.new(minutes, ui)
    init_plugins

  end

  def init_plugins
    puts "init plugins"
    @plugins = []
    ::Plugins.constants.each do |name|
      @plugins << ::Plugins.const_get(name).new(self)
    end
  end

  def start
    timer.start
  end
end

class StdioUi
  def notify(text)
    puts text
  end
end

SleepTimer = Struct.new(:minutes, :notifier) do
  def start
    sleep minutes * 60
    notifier.notify("Tea is ready!")
  end
end

module Plugins
  class Beep    
    def initialize(tea_clock)

      tea_clock.ui.extend(UiWithBeep)
    end

    module UiWithBeep
      def notify(*)         #gets called by notifier.notify("Tea is ready")
        puts "BEEP!"

        super               #calls notify in class StdioUi
      end
    end
  end
end

t = TeaClock.new(0.01).start

2 个答案:

答案 0 :(得分:1)

每个类都有一个名为ancestors的属性,表示继承链。 ruby遍历继承行为列表并查找匹配方法。如果找到一个,则用给定的参数调用它。如果你拨打super,它会查找下一场比赛。

1.9.3-p194 :003 > String.class.ancestors
 => [Class, Mocha::ClassMethods, Module, NewRelic::Agent::MethodTracer::InstanceMethods, NewRelic::Agent::MethodTracer::InstanceMethods::TraceExecutionScoped, NewRelic::Agent::MethodTracer::ClassMethods, NewRelic::Agent::MethodTracer::ClassMethods::AddMethodTracer, Mocha::ModuleMethods, ActiveSupport::Dependencies::ModuleConstMissing, Object, FactoryGirl::Syntax::Vintage, Metaclass::ObjectMethods, Mocha::ObjectMethods, PP::ObjectMixin, JSON::Ext::Generator::GeneratorMethods::Object, ActiveSupport::Dependencies::Loadable, FriendlyId::ObjectUtils, Kernel, BasicObject]

答案 1 :(得分:1)

本书:我一直推荐这本优秀的书Metaprogramming Ruby。我在撰写这个答案时正在咨询它。


所以,在这里你用一个模块扩展一个对象。在Ruby中,它被称为对象扩展。在简单的情况下,它都按预期工作,如下所示:

module Foo
  def hello
    puts "foo"
  end
end

class Bar
end

bar = Bar.new
bar.extend Foo
bar.hello
# >> foo

当涉及到类自己的方法时,事情会变得复杂。这是您的代码段的简化版本,表现出相同的行为。

module Foo
  def hello
    puts "foo"
    super
  end
end

class Bar
  def hello
    puts 'bar'
  end
end

bar = Bar.new
bar.extend Foo
bar.hello
# >> foo
# >> bar

当您在ruby中调用方法时,解释器必须首先找到要调用的方法。这称为方法查找。现在,当你定义一个实例方法时,实际上它是类对象的一个​​方法,而不是那个实例。因此,对于第一个片段,方法查找就像这样:

 1) bar instance => method hello not found here
 2) Bar class => method hello found

但是,在扩展对象时,方法会注入实例的eigenclass。它是一个特殊的“隐藏”类,对每个实例都是唯一的。实际上,方法查找首先要经过它。第一个片段:

 1) bar instance => method hello not found here
 2) bar's eigenclass => method hello not found here
 3) Bar class => method hello found

现在应该清楚为什么调用Foo.hello而不是Bar.hello:因为它出现在方法查找过程的前面!

 1) bar instance => method hello not found here
 2) bar's eigenclass => method hello found

我可能犯了一些错误,但这大致是发生的事情。