在Ruby里用到对象图导航(Object Graph Navigation)的时候,有时候需要判断对象是否为nil,很常见这样的代码:
#显示某个产品的分类名称
product.category ? product.category.name : nil
ozmm.org的chris最近介绍了一个好方法,他给这个方法起名叫
try(),给Object添加一个try方法:
class Object
def try(method)
send method if respond_to? method
end
end
这样上面的代码就可以简化成
product.category.try(:name)
这个try的实际用途很多,比如:
#删除某个可能存在的用户
User.find_by_name("JavaEye").try(:destroy)
#找出最后一个未激活用户的名字
User.find_all_by_active(false).last.try(:name)
但是这个简单的try()有很多限制,比如原先这样的代码就不能解决:
#默认值
product.category ? product.category.name : "N/A"
#多层对象图导航
product.category.owner.name
对此Anders Engström提供了一个
Improved 'try()',上面的代码可以简化成
product.category.try(:name, :default => "N/A")
product.category.try(:owner, :name)
但是又有人觉得这样不够直观了,提供了一个类似Groovy做法的:
A better “try()” for Ruby, why not do the Groovy way?
Groovy语言本身提供了内置的?.操作: person?.name,而上面这篇文章则通过在方法名后面添加"_"来实现相同的目的
product.category.owner_.name_
Q1: 和方法2相比,它少了一个默认值的处理,大家觉得添加这样的特性如何?
product.category.name_(:default => "N/A")
上面这3种try()方法都是通过method missing实现的,这篇文章提到的
Maybe Monad也可以解决这个问题:
Maybe.new(product).category.name.value("N/A")
Q2: 这些方法你更喜欢用哪一种呢?或者你有其他更好的方法?欢迎讨论。
我比较喜欢方法2,感觉代码侵入比较小,缺点是多层导航的时候不够直观。而方法4Maybe Monad的优缺点正好与之相反,如果能综合这2种方法就好了。