地址:http://www.alfajango.com/blog/method_missing-a-rubyists-beautiful-mistress/
标题:method_missing: 
A Rubyist’s Beautiful Mistress
译者:逸才

我最近读了些文章(比如这篇),宣传在 Ruby 里使用 method_missing 的。

很多人都与 method_missing 干柴烈火,但在并没有小心处理彼此之间的关系。所以,我想来探讨一下这个问题:

** 我该怎么用 method_missing **

什么时候该抵挡 method_missing 的诱惑

首先,永远不要在还没花时间考虑你用得够不够好之前,就向 method_missing 的魅力屈服。你知道,在日常生活中,很少会让你以为的那样亟需 method_missing

日常:方法代理

案例:我需要让这个类能够使用另一个类的方法

这是我所见过最普遍的使用 method_missing 的情况。这在 gems 与 Rails 插件里头尤其流行。它的模型类似这样:

class Adef hiputs "Hi from #{self.class}"end
endclass Bdef initialize@b = A.newenddef method_missing(method_name, *args, &block)@b.send(method_name, *args, &block)end
endA.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 Bdefine_method(:hi) do@b.hiend
end

“可是我有一大坨方法要定义!” 你抱怨

“没问题!” 我卖萌眨眼

class B[:hi, :bye, :achoo, :gesundheit].each do |name|define_method(name) do@b.send(name)endend
end

可是我懒得把它们一个个写出来!

你有点难搞哦

class A# ... lots of methods in here
end
class BA.instance_methods.each do |name|define_method(name) do@b.send(name)endend
end

那假如我要定义的方法跟原本的有那么一些些不一样呢?

容易

class Adef hiputs "Hi."end
endclass BA.instance_methods.each do |name|define_method("what_is_#{name}") doif @b.respond_to?(name)@b.send(name)elsefalseendendend
endB.new.what_is_hi #=> "Hi."
B.new.what_is_wtf #=> false

呃,代码看起来不优雅啊

那就没办法了,凑合得了。如果你想要代码更易读,可以看看我们的 ruby delegation library 和 Rails ActiveRecord delegation。

好,我们总结一下,看看 define_method 的真正威力。

修改自 ruby-doc.org 上的 例子

class Adef fredputs "In Fred"enddef create_method(name, &block)self.class.send(:define_method, name, &block)enddefine_method(:wilma) { puts "Charge it!" }
end
class B < Adefine_method(:barney, instance_method(:fred))
enda = B.new
a.barney                                #=> In Fred
a.wilma                                 #=> Charge it!
a.create_method(:betty) { p self.to_s }
a.betty                                 #=> B

什么时候用 method_missing

现在你估计在想,总有该用它的时候吧,不然还要它干嘛?没错。

动态命名的方法(又名,元方法)

案例:我要依据某种模式提供一组方法。这些方法做的事情顾名思义。我可能从来没有调用过这些可能的方法,但是等我要用的时候,它们必须可用。

现在才是人话!这其实正是 ActiveRecord 所采用的方式,为你提供那些基于属性的动态构建的查找方法,比如find_by_login_and_email(user_login, user_email)

def method_missing(method_id, *arguments, &block)if match = DynamicFinderMatch.match(method_id)attribute_names = match.attribute_namessuper unless all_attributes_exists?(attribute_names)if match.finder?# ...you get the pointend # my OCD makes me unable to omit this# ...elsesuper # this is important, I'll tell you why in a secondend
end

权衡利弊

当你有一大堆元方法要定义,又不一定用得到的时候,method_missing 是个完美的折衷。

想想 ActiveRecord 中基于属性的查找方法。要用 define_method 从头到脚定义这些方法,ActiveRecord 需要检查每个模型的表中所有的字段,并为每个可能的字段组合方式都定义方法。

find_by_email
find_by_login
find_by_name
find_by_id
find_by_email_and_login
find_by_email_and_login_and_name
find_by_email_and_name
# ...

假如你的模型有 10 个字段,那就是 10! (362880)个查找方法需要定义。想象一下,在你的 Rails 项目跑起来的时候,有这么多个方法需要一次定义掉,而 ruby 环境还得把它们都放在内存里头。

老虎·伍兹都做不来的事情。

** 正确的 method_missing 使用方式

(译者猥琐地注:要回家了,以下简要摘译)

1、先检查

并不是每次调用都要处理的,你应该先检查一下这次调用是否符合你需要添加的元方法的模式:

def method_missing(method_id, *arguments, &block)if method_id.to_s =~ /^what_is_[\w]+/# do your thingend
end

2、包起来

检查好了,确实要处理的,请记得把函数体包在你的好基友,define_method 里面。如此,下次就不用找情妇了:

def method_missing(method_id, *arguments, &block)if method_id.to_s =~ /^what_is_[\w]+/self.class.send :define_method, method_id do# do your thingendself.send(method_id)end
end

3、擦屁股

自己处理不来的方法,可能父类有办法,所以 super 一下:

def method_missing(method_id, *arguments, &block)if method_id.to_s =~ /^what_is_[\w]+/self.class.send :define_method, method_id do# do your thingendself.send(method_id)elsesuperend
end

4、昭告天下

def respond_to?(method_id, include_private = false)if method_id.to_s =~ /^what_is_[\w]+/trueelsesuperend
end

要告诉别人,你的类虽然暂时还没有这个方法,但是其实是能够响应这方法的。

** 总结 **

在每个 Ruby 程序员的生活中,这仨方法扮演了重要的角色。define_method 是你的好基友,method_missing 是个如胶似漆但也需相敬如宾的情妇,而 respond_to? 则是你的爱子,如此无虞。

method_missing,一个 Ruby 程序员的梦中情人相关推荐

  1. 《Ruby程序员修炼之道》(第2版)—第1章1.1节进入Ruby的世界

    本节书摘来自异步社区<Ruby程序员修炼之道>一书中的第1章,第1.1节进入Ruby的世界,作者[美]David A. Black(戴维 A. 布莱克),更多章节内容可以访问云栖社区&qu ...

  2. 《Ruby程序员修炼之道》(第2版)—第1章1.2节剖析Ruby的安装

    本节书摘来自异步社区<Ruby程序员修炼之道>一书中的第1章,第1.2节剖析Ruby的安装,作者[美]David A. Black(戴维 A. 布莱克),更多章节内容可以访问云栖社区&qu ...

  3. 《Ruby程序员修炼之道》(第2版)目录—导读

    版权 Ruby程序员修炼之道(第2版) • 著 [美] David A. Black 译 钟凤鸣 陈雪静 责任编辑 杨海玲 • 人民邮电出版社出版发行 北京市丰台区成寿寺路11号 邮编 100164 ...

  4. 《Ruby程序员修炼之道》(第2版)—第1章1.4节易用的Ruby工具和应用程序

    本节书摘来自异步社区<Ruby程序员修炼之道>一书中的第1章,第1.4节易用的Ruby工具和应用程序,作者[美]David A. Black(戴维 A. 布莱克),更多章节内容可以访问云栖 ...

  5. 一个游戏程序员的学习资料 (zz)

    一个游戏程序员的学习资料 //z 2012-4-19 14:39:51 PM IS2120@CSDN 想起写这篇文章是在看侯杰先生的<深入浅出MFC>时, 突然觉得自己在大学这几年关于游戏 ...

  6. 转载 一个游戏程序员的学习资料

    转载]一个游戏程序员的学习资料 2008-05-15 20:31 日志原文:http://xoyojank.blog.sohu.com/83788512.html 想起写这篇文章是在看侯杰先生的< ...

  7. 一个游戏程序员的学习资料[转]

    http://software.intel.com/zh-cn/blogs/2012/03/20/400010004/?cid=sw:prccsdn2194 一个游戏程序员的学习资料 作者: weiq ...

  8. 某百度程序员中午面试一个阿里程序员,晚上去阿里面试,面试官竟是中午那个人!

    世界之大无奇不有,有些巧合简直就是天作之合,就像下面这位百度程序员和阿里程序员的故事. 某百度程序员发帖吐槽:中午面试了一个阿里程序员,问了他一些刁钻的问题,结果自己晚上去阿里面试,面试官竟然就是中午 ...

  9. 某百度程序员中午面试一个阿里程序员,晚上去阿里面试,面试官竟是中午那个人!...

    世界之大无奇不有,有些巧合简直就是天作之合,就像下面这位百度程序员和阿里程序员的故事. 某百度程序员发帖吐槽:中午面试了一个阿里程序员,问了他一些刁钻的问题,结果自己晚上去阿里面试,面试官竟然就是中午 ...

最新文章

  1. linux Makefile 中使用 shell命令
  2. Spring Cloud Data Flow 中的 ETL
  3. mybatis学习7之动态sql
  4. Vue的报错Echarts Cannot read property ‘init‘ of undefined
  5. How is ABAP keyword highlight implemented in Chrome
  6. java禁止js获取cookie_java中Cookie被禁用后Session追踪问题
  7. 基于图查询系统的图计算引擎
  8. MySQL运行一段时间后自动停止问题的排查
  9. java中的Vector的用法
  10. 【clion】自定义优美的代码配色(主题)
  11. delphi多线程TThread详解
  12. 如何使用Python提取pdf表格及文本,并保存到excel
  13. 手机号 mysql 索引_mysql索引以及优化
  14. 领英精灵和领英助理哪个好,看这一篇就够了
  15. 超简单的java短信验证码,神级之作
  16. 华为设备配置IS-IS的负载分担
  17. 【51单片机】独立按键,每个按键不同功能,数码管数值的加减,控制流水灯模式,包含按键消抖,数码显示,流水灯
  18. ARM SMMU的原理与IOMMU[转载]
  19. 使用docker部署ETH区块链浏览器
  20. 输出二叉查找树poj 2418 二叉查找树

热门文章

  1. win系统,复制文件提示对于目标文件系统过大
  2. CSS 实现一个3d魔方
  3. SSD Win8 系统盘 4K 无损对齐历险记
  4. 14年中,13寸MacBook pro外接显卡坞(技嘉aorus gaming box)经验
  5. 90页第三题,创建一个 Rectangle类,添加width和lenght两个成员变量 在 Rectangle类中添加两种方法分别计算矩形的周长和面积 ,编程利用Rectangle输出一个矩形的
  6. 腾讯牵头成立CSA云原生安全工作组,助力标准制定和产业落地
  7. Ventuz教程学习笔记之介绍
  8. 类似爱库存S2B2C的电商源码
  9. 学python心得体会1000字-浅谈我对python的学习感受
  10. NLP——part of speech (POS)中的隐马尔可夫模型 + Viterbi 算法