ruby 寻找方法时 先右后上。先找类,再找父类...
如果使用include/prepend 则变成
测试用例
class Super_class
def func
puts "func from SuperClass"
end
end
module Module_prepend
def func
puts "func from Module_prepend"
super
end
end
module Module_include
def func
puts "func from Module_include"
super
end
end
class Kclass < Super_class
include Module_include
prepend Module_prepend
def func
puts "func from Class"
super
end
end
Kclass.new.func
=begin
将会输出
func from Module_prepend
func from Class
func from Module_include
func from SuperClass
=end
#如果有个Work#do 需要我们监视它的耗时情况,原始的工作情况如下
class VirtaualWork
def do(&block)
puts "VirtaualWork#do"
block.call block if block_given?
end
end
class Work < VirtaualWork
def do
puts "Work#do"
sleep(rand(3))
super
end
end
Work.new.do { puts "work end at #{Time.now}" }
#Work#do
#VirtaualWork#do
#work end at 2016-08-02 18:23:54 +0800
当然我们需要的不是修改原始的do方法,而是扩展Work类,使它具有新的能力。现在重新打开Work
class Work
include Watch_filter
watch_method :do
end
等等 Watch_filter 是什么鬼,watch_method又是什么东东,其实我们要稍后才会实现它。从字面上理解我们只是说我们希望Work能有一个新的能力,能监视do方法,简单的在方法被调用之前记下时间,在方法被调用之后能查出耗时并输出。这个扩展的能力将由Watch_filter module提供,下面我们来看看这个模块的实现
module Watch_filter
def self.included(kclass)
#Module.included是一个回调方法,当其它的类或模块include了此模块时,将会回调此方法
kclass.extend(Ext_methods)
end
module Ext_methods
#mixin Watch_filter模块的类都将会有新的Klass.watch_method方法的能力
def watch_method(method_name)
#prepend一个匿名模块。重新定义method_name方法
prepend_mod=Module.new do
define_method(method_name) do |*args,&block|
puts "prepend module##{method_name}"
instance_variable_set(:@start_method_time,Time.now)
super(*args,&block)
end
end
prepend prepend_mod
#include 一个匿名模块,重新定义method_name方法
include_mod= Module.new do
define_method(method_name) do |*args,&block|
puts "include module##{method_name}"
super(*args,&block)
puts "work#do spend time #{Time.now - @start_method_time}"
end
end
include include_mod
end
end
end
class Work
include Watch_filter
watch_method :do
end
work=Work.new.do { puts "work end at #{Time.now}" }
#prepend module#do
#Work#do
#include module#do
#VirtaualWork#do
#work end at 2016-08-02 18:23:55 +0800
#work#do spend time 1.00316
魔法在这里
puts Work.ancestors.to_s
[#<Module:0x007fdf8b9362b0>, Work, #<Module:0x007fdf8b9360f8>, Watch_filter, VirtaualWork, Object, Kernel, BasicObject]
work#do会先找到prepend module的do方法,然后由super一直上溯到VirtaualWork