1

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

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

如果您查看下面的代码,您会发现 中有一个 notify 方法class StdioUi以及module UiWithBeep. 上面显示的 start 方法调用 in 中的 notify 方法module UiWithBeep,然后通过 'super' 调用 in 中的 notify 方法class StdioUi。(效果是在“Tea is ready”之前听到“BEEP!”。)但是,我不明白为什么 notifier.notify 调用 notify 方法是 inmodule UiWithBeep而不是 in class StdioUi第一个问题:它如何知道去一个“通知”而不是另一个。 SecondQuestion而且,虽然我理解超级,但建立关系以便通知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
4

2 回答 2

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]
于 2012-10-17T19:21:47.620 回答
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

我可能犯了一些错误,但这大致就是发生了什么。

于 2012-10-17T19:40:38.793 回答