2. 动态性
动态性是Ruby的灵魂。Ruby的动态性体现在很多方面,Object类中也有一个最根本的体现,就是send方法,由于这个方法如此重要,为了使得在这个方法被覆盖时你可以同样获得这种功能,Object还提供了一个__send__方法,它们完成同样的功能。
class SayHello
def hello(name)
puts "hello, #{name}."
end
end
sayHello = SayHello.new
sayHello.send("hello", "Bob") #=>"hello, Bob."
sayHello.__send__("hello", "Bob") #=>"hello, Bob."
很显然,通过调用send方法,我们可以获得和调用hello方法一样的能力。但是,它更强大,send方法是动态的,就是说,我们可以动态调用任何存在的方法。你大概联想到C语言中的函数指针了吧,或者你联想到Java和.Net世界中的反射了吧。Ruby要简洁得多,不是么?而且,你甚至还可以调用不存在的方法!你如果使用过Rails,一定会好奇Rails的模型中的find方法族吧,你不光可以调用find方法,你还可以调用findBy[Column_Name]来根据你模型中的一列或若干列来进行查找,而你根本没有定义过这些方法!
魔法的秘密在于method_missing,这个方法会在当send方法找不到传入的方法名时被调用,在Object对象中,这个方法会抛出一个NoMethodError的错误,但是,你可以利用它来搞一些诡计,比如我们想在上面的类中实现可以调用hello_[Name]方法来对给定的人说“hello”。
class SayHello
def hello(name)
puts "hello, #{name}."
end
alias old_method_missing method_missing
def method_missing(method_id)
if match = /hello_([a-zA-Z]/w*)/.match(method_id.to_s)
hello(match.captures.to_s)
else
old_method_missing(method_id)
end
end
end
sayHello = SayHello.new
sayHello.send("hello", "Bob") #=>"hello, Bob."
sayHello.send("hello_Bob") #=>"hello, Bob."
sayHello.hello_Bob #=>"hello, Bob."
sayHello.wrongMethod #=>NoMethodError occurs.
当然,这里的例子相对还是比较简单,比如只支持一个名字,你显然可以用自己的方式把它定义的更加像样。Rails的ActiveRecord::Base类使用了相似的技术来实现那些魔术般的find方法族,只是更加复杂些。