Ruby流程控制: 抛出异常、返回nil还是让其失败?

5

我正在考虑流程控制的最佳实践。我应该采取哪种方法?

1)不检查任何内容,让程序失败(代码更简洁,自然的错误信息):

  def self.fetch(feed_id)
    feed = Feed.find(feed_id)
    feed.fetch
  end

2) 返回nil以默默失败 (然而,“Clean Code”认为,您永远不应该返回null):

  def self.fetch(feed_id)
    return unless feed_id
    feed = Feed.find(feed_id)
    return unless feed
    feed.fetch
  end

3) 抛出异常(因为按id查找Feed不成功是个例外情况):

  def self.fetch(feed_id)
    raise ArgumentError.new unless feed_id
    feed = Feed.find(feed_id)
    raise ArgumentError.new unless feed
    feed.fetch
  end

换句话说,我应该积极使用守卫条件,还是更好地依赖于Ruby / Rails方法,让它们在出现问题时抛出异常?

1
如果使用ActiveRecord,find会在id不存在时报错,而find_by_id不会报错。 - tokland
3个回答

6

1) 不做任何检查,让程序失败(代码更简洁,错误信息更自然):

在已知、记录的异常情况下,“让程序失败”是可以接受的,但因为试图使用一个空对象而获得不愉快的NoMethodError,这只是粗心大意。在您的特定示例中,ActiveRecord#find引发了一个记录在案的ActiveRecord::RecordNotFound异常,所以我认为这是正确的方式:

def self.fetch(feed_id)
  Feed.find(feed_id).fetch
end

2) 如果失败了,通过返回 nil 来默默处理(然而,《Clean Code》指出,你永远不应该返回 null):

这虽然是一条通用的建议,但 Ruby 中充斥着返回nil的方法,这没关系(只要有文档记录),它只表示“什么也没有”(并且允许使用非常紧凑的模式something_that_can_be_nil || another_value)。在这种情况下,我会使用 Ick 的maybe来简洁地编写代码:

def self.fetch(feed_id)
  Feed.find_by_id(feed_id).maybe.fetch
end

3) 抛出异常(因为根据id查找feed是异常情况):

是的,但是让方法抛出众所周知的RecordNotFound异常,而不是自定义异常(除非你想要抽象出你正在使用AR的事实,这可能非常麻烦)。


2
我认为正确的答案是:这取决于情况。理论上,用户不应该从框架中遇到任何错误消息。您必须始终准备处理这些异常。选择权完全属于您(如果它不是外部使用的接口或其他内容)。
如果您选择第一种方法,我认为您应该首先查询是否存在具有该ID的任何源,然后尝试获取它。如果在两者之间源消失了,那么报告问题可能会成为一个真正的问题。第三个基本上是相同的。您需要确保已处理每种情况,并且在那种情况下,抛出异常可以帮助防止用户看到错误。
第二个解决方案基本上就是这样,但是采用内部处理方式。通过使用nil,您发出某些问题的信号。它也必须被处理,向用户报告或进行其他处理。缺点是如果您忘记了这一点,您可能会误导用户。
我会使用第一种方法,额外进行检查以确保它存在。但这取决于用途。

是的,这取决于情况。如果这是一个致命错误,操作必须成功才能使程序继续运行,请让它失败或抛出异常。否则,只需返回nil。 - SwiftMango

2
我会选择简洁的版本。
如果您没有向“fetch”方法提供“feed_id”,Ruby本身将引发“ArgumentError: wrong number of arguments(0 for 1)”异常,因此第3部分的第一部分是无意义的。
如果您没有提供有效的“feed_id”,那么“Feed.find(feed_id)”调用将引发另一个异常,很可能是带有消息的“ActiveRecord::RecordNotFound”,其中指出它无法找到具有提供的ID的源,或者如果未提供ID(“feed_id”参数为“nil”),则表示无法找到没有ID的源。
对我来说,使用“feed_id = nil”调用该方法似乎有点愚蠢,因此我可能会认为“如果您发送无效输入,则可能会出现错误”,在这种情况下,我认为“ActiveRecord::RecordNotFound”将为您提供更多关于出错原因的信息,而不是引发“ArgumentError”。
返回空值很少是一件好事,因为这不会告诉您实际出了什么问题。因此,我也排除了#2。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接