详解Ruby元编程之method_missing
说到method_missing大家都知道是Ruby元编程的梦中情人,但是有时候程序员病没有很小心的处理他们之间的关系,下面就和爱站技术频道小编一起来探讨一下吧!
** 我该怎么用 method_missing **
什么时候该抵挡 method_missing 的诱惑
首先,永远不要在还没花时间考虑你用得够不够好之前,就向 method_missing 的魅力屈服。你知道,在日常生活中,很少会让你以为的那样亟需 method_missing:
日常:方法代理
案例:我需要让这个类能够使用另一个类的方法
这是我所见过最普遍的使用 method_missing 的情况。这在 gems 与 Rails 插件里头尤其流行。它的模型类似这样:
class A
def hi
puts "Hi from #{self.class}"
end
end
class B
def initialize
@b = A.new
end
def method_missing(method_name, *args, &block)
@b.send(method_name, *args, &block)
end
end
A.new.hi #=> Hi from A
B.new.hi #=> Hi from A
如此,B 就拥有了 A 的所有实例方法。但是让我们想想,在调用 @b.hi 的时候都发生了什么。你的 ruby 环境沿着继承链一路找 hi 这个方法,到最后,恰恰在丢出个 NoMethodError 前,它调了 method_missing 这个方法。
在上例中,情况并不坏,毕竟这里就两个微不足道的类需要查。但通常,我们是在 Rails 或者其他一些框架的上下文中编程。而你的 Rails 模型继承自 ActiveRecord,而它又集成自其他一大坨的类,于是现在你就有了一坨高高的堆栈要爬⋯⋯ 在你每次调用 @b.hi 的时候!
你的好基友:define_method
估计现在你在抱怨,“但是史蒂夫,我需要 method_missing” 我告诉你,别忘了其实除了情妇之外,你还有个忠诚的好基友,叫做 define_method。
它允许你动态地定义一个方法(顾名思义)。它的伟大之处在于,在它执行过之后(通常在你的类们加载之后),这些方法就存在你的类中了,简单直接。在你创建这些方法的时候,也没有什么继承链需要爬。
define_method 很有爱很可靠,并且能够满足你的日常生活。不信我?接着看⋯⋯
class B
define_method(:hi) do
@b.hi
end
end
“可是我有一大坨方法要定义!” 你抱怨
“没问题!” 我卖萌眨眼
class B
[:hi, :bye, :achoo, :gesundheit].each do |name|
define_method(name) do
@b.send(name)
end
end
end
可是我懒得把它们一个个写出来!
你有点难搞哦
class A
# ... lots of methods in here
end
class B
A.instance_methods.each do |name|
define_method(name) do
@b.send(name)
end
end
end
那假如我要定义的方法跟原本的有那么一些些不一样呢?
容易
class A
def hi
puts "Hi."
end
end
class B
A.instance_methods.each do |name|
define_method("what_is_#{name}") do
if @b.respond_to?(name)
@b.send(name)
else
false
end
end
end
end
B.new.what_is_hi #=> "Hi."
B.new.what_is_wtf #=> false
呃,代码看起来不优雅啊
那就没办法了,凑合得了。如果你想要代码更易读,可以看看我们的ruby delegation library 和 Rails ActiveRecord delegation。
好,我们总结一下,看看 define_method 的真正威力。
class A
def fred
puts "In Fred"
end
def create_method(name, &block)
self.class.send(:define_method, name, &block)
end
define_method(:wilma) { puts "Charge it!" }
end
class B < A
define_method(:barney, instance_method(:fred))
end
案例:我要依据某种模式提供一组方法。这些方法做的事情顾名思义。我可能从来没有调用过这些可能的方法,但是等我要用的时候,它们必须可用。
当你有一大堆元方法要定义,又不一定用得到的时候,method_missing 是个完美的折衷。
并不是每次调用都要处理的,你应该先检查一下这次调用是否符合你需要添加的元方法的模式:
检查好了,确实要处理的,请记得把函数体包在你的好基友,define_method 里面。如此,下次就不用找情妇了:
自己处理不来的方法,可能父类有办法,所以 super 一下:
要告诉别人,你的类虽然暂时还没有这个方法,但是其实是能够响应这方法的。
在每一个Ruby程序员的生活中,这三种方法都起着重要的作用,大家都记起来了没有呢?可以收藏爱站技术频道,方便大家查阅技术知识!