在下面的茶计时器代码中,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
答案 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
我可能犯了一些错误,但这大致是发生的事情。