我最近读了些文章(比如),宣传在 ruby 里使用 method_missing 的。
很多人都与 method_missing 干柴烈火,但在并没有小心处理彼此之间的关系。所以,我想来探讨一下这个问题:
** 我该怎么用 method_missing **
什么时候该抵挡 method_missing 的诱惑
首先,永远不要在还没花时间考虑你用得够不够好之前,就向 method_missing 的魅力屈服。你知道,在日常生活中,很少会让你以为的那样亟需 method_missing:
日常:方法代理
案例:我需要让这个类能够使用另一个类的方法
这是我所见过最普遍的使用 method_missing 的情况。这在 gems 与 rails 插件里头尤其流行。它的模型类似这样:
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
在上例中,情况并不坏,毕竟这里就两个微不足道的类需要查。但通常,我们是在 rails 或者其他一些框架的上下文中编程。而你的 rails 模型继承自 activerecord,而它又集成自其他一大坨的类,于是现在你就有了一坨高高的堆栈要爬⋯⋯ 在你每次调用 @b.hi 的时候!
你的好基友:define_method
估计现在你在抱怨,“但是史蒂夫,我需要 method_missing” 我告诉你,别忘了其实除了情妇之外,你还有个忠诚的好基友,叫做 define_method。
它允许你动态地定义一个方法(顾名思义)。它的伟大之处在于,在它执行过之后(通常在你的类们加载之后),这些方法就存在你的类中了,简单直接。在你创建这些方法的时候,也没有什么继承链需要爬。
define_method 很有爱很可靠,并且能够满足你的日常生活。不信我?接着看⋯⋯
“没问题!” 我卖萌眨眼
你有点难搞哦
容易
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
那就没办法了,凑合得了。如果你想要代码更易读,可以看看我们的 和 。
好,我们总结一下,看看 define_method 的真正威力。
修改自 ruby-doc.org 上的
a = b.new
a.barney #=> in fred
a.wilma #=> charge it!
a.create_method(:betty) { p self.to_s }
a.betty #=> b
现在你估计在想,总有该用它的时候吧,不然还要它干嘛?没错。
动态命名的方法(又名,元方法)
案例:我要依据某种模式提供一组方法。这些方法做的事情顾名思义。我可能从来没有调用过这些可能的方法,但是等我要用的时候,它们必须可用。
现在才是人话!这其实正是 activerecord 所采用的方式,为你提供那些基于属性的动态构建的查找方法,比如 find_by_login_and_email(user_login, user_email)。
权衡利弊
当你有一大堆元方法要定义,又不一定用得到的时候,method_missing 是个完美的折衷。
想想 activerecord 中基于属性的查找方法。要用 define_method 从头到脚定义这些方法,activerecord 需要检查每个模型的表中所有的字段,并为每个可能的字段组合方式都定义方法。
假如你的模型有 10 个字段,那就是 10! (362880)个查找方法需要定义。想象一下,在你的 rails 项目跑起来的时候,有这么多个方法需要一次定义掉,而 ruby 环境还得把它们都放在内存里头。
老虎·伍兹都做不来的事情。
** 正确的 method_missing 使用方式
(译者猥琐地注:要回家了,以下简要摘译)
1、先检查
并不是每次调用都要处理的,你应该先检查一下这次调用是否符合你需要添加的元方法的模式:
检查好了,确实要处理的,请记得把函数体包在你的好基友,define_method 里面。如此,下次就不用找情妇了:
自己处理不来的方法,可能父类有办法,所以 super 一下:
4、昭告天下
** 总结 **
在每个 ruby 程序员的生活中,这仨方法扮演了重要的角色。define_method 是你的好基友,method_missing 是个如胶似漆但也需相敬如宾的情妇,而 respond_to? 则是你的爱子,如此无虞。
如对本文有疑问, 点击进行留言回复!!
自动化部署Ruby on Rails应用(docker + jenkins)
网友评论