我最近读了些文章(比如这篇),宣传在 Ruby 里使用 method_missing 的。
很多人都与 method_missing 干柴烈火,但在并没有小心处理彼此之间的关系。所以,我想来探讨一下这个问题:
** 我该怎么用 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,而它又集成自其他一大坨的类,于是现在你就有了一坨高高的堆栈要爬"codetitle">复制代码 代码如下:
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"Hi."
B.new.what_is_wtf #=> false
呃,代码看起来不优雅啊
那就没办法了,凑合得了。如果你想要代码更易读,可以看看我们的ruby delegation library 和 Rails ActiveRecord delegation。
好,我们总结一下,看看 define_method 的真正威力。
修改自 ruby-doc.org 上的 例子
复制代码 代码如下:
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
a = 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_names
super unless all_attributes_exists"codetitle">复制代码 代码如下:
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 thing
end
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 thing
end
self.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 thing
end
self.send(method_id)
else
super
end
end
4、昭告天下
复制代码 代码如下:
def respond_to?(method_id, include_private = false)
if method_id.to_s =~ /^what_is_[\w]+/
true
else
super
end
end
要告诉别人,你的类虽然暂时还没有这个方法,但是其实是能够响应这方法的。
** 总结 **
在每个 Ruby 程序员的生活中,这仨方法扮演了重要的角色。define_method 是你的好基友,method_missing 是个如胶似漆但也需相敬如宾的情妇,而 respond_to? 则是你的爱子,如此无虞。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]