【问题标题】:Raise custom exceptions, return constants, or return symbols? Why?引发自定义异常、返回常量或返回符号?为什么?
【发布时间】:2012-03-22 10:46:16
【问题描述】:

假设我有一个WebCrawler 课程。它可能会遇到几个错误。我应该如何向上传播错误?

使用异常:

class WebCrawler
  class UrlBadFormatError < StandardError; end
  class PageNotFoundError < StandardError; end
  class UnauthorizedError < StandardError; end
  def crawl(url)
    if(! url =~ /some_format/)
      raise UrlBadFormatError
    response = get(url)
    if(response.code == 404)
      raise PageNotFoundError
    if(response.code == 403)
      raise UnauthorizedError
    ... 
  end
end

或常量:

class WebCrawler
  URL_BAD_FORMAT = 1
  PAGE_NOT_FOUND = 2
  UNAUTHORZIED = 3 
  def crawl(url)
    if(! url =~ /some_format/)
      return URL_BAD_FORMAT
    response = get(url)
    if(response.code == 404)
      return PAGE_NOT_FOUND
    if(response.code == 403)
      return UNAUTHORZIED
    ... 
  end
end

或符号:

class WebCrawler
  def crawl(url)
    if(! url =~ /some_format/)
      return :url_bad_format
    response = get(url)
    if(response.code == 404)
      return :page_not_found
    if(response.code == 403)
      return :unauthorized
    ... 
  end
end

哪个最好?还是取决于(取决于什么?)

【问题讨论】:

    标签: ruby exception error-handling


    【解决方案1】:

    我反对在网络上遇到 403、404、格式错误的 url 和类似的常见事件时使用异常。异常适用于“内部”错误。在 World Wild Web 中,错误的 URL 完全没有例外。应该有一种方法来处理每种不同的 URL 疾病。我个人会以符号的形式返回特殊值,或者一些记录所发生情况的“SpecialCase”对象。还有未充分利用的 catch...throw 语句。

    【讨论】:

    • 仅仅因为错误发生很多,并不意味着它不是例外。如果汽车因为电池没电而无法启动,这是正常行为吗?是的,但这是一种错误情况,需要特殊情况进行补救。不要把你的坏习惯和最佳实践混为一谈。通信故障是例外,无论它们发生的频率如何。失败!=成功。
    • 我的原因从我的回答中并不明显,所以也许它毕竟不是那么好 :-) 更糟糕的是,我自己忘记了 :-) 但我的习惯不应该受到责备,因为我喜欢例外——不仅在电池没电的时候,而且在路灯变红的时候。有one discussion of this on SO,明确提到Sinatra 使用catch/throw 处理HTTP 异常,还有I started another one。接球/投球真的很糟糕,还是没有得到充分利用?
    【解决方案2】:

    对于指示程序员错误的东西,例如传递给方法的参数类型错误,肯定会抛出异常。该异常会使程序崩溃,从而将程序员的注意力吸引到他们错误地使用您的类这一事实上,因此他们可以解决问题。在这种情况下,返回错误代码是没有意义的,因为程序必须包含代码来检查返回值,但是在程序被调试之后,这样的错误不应该发生。

    在您的WebCrawler 类中,是否预期 crawl 有时会收到错误的 URL 作为参数?我想答案可能是否定的。因此,当传递了错误的 URL 时,引发异常是适当的。

    当引发异常时,执行流程会突然“跳转”到最里面的处理程序。当大多数情况下预计会发生异常时,这可能是一种构建代码的有用方法,因为您可以将方法的“主要流程”编写为简单的直线代码,而无需包含当一些罕见的错误情况发生时会发生什么的很多细节。这些细节可以从“主要流程”代码中分离出来,并放入异常处理程序中。但是,当预期在正常情况下会发生错误情况时,最好将错误处理代码放入“主要流程”中,以便更清楚地了解正在发生的事情。如果您的程序的控制流“跳来跳去”(就像将异常用于正常流控制的情况一样),这意味着读者还必须在程序文本中“跳来跳去”,因为他们正在弄清楚它是如何工作的。

    对于其他两个,我认为至少在某些时候,HTTP 请求会返回错误代码。为了确定异常或特殊返回值是否是指示这种情况的最佳方式,我会考虑在正常使用情况下这些情况发生的频率多久。还要考虑客户端代码将如何读取任何一种方式。如果您使用异常,他们将不得不编写如下内容:

    urls.map do |url|
      begin
        crawl(url)
      rescue PageNotFoundError
        ""
      rescue UnauthorizedError
        ""
      end
    end
    

    (顺便说一句,我认为这个代码示例显示了一些东西:如果您的两个自定义异常都继承自一个公共超类,这可能是一个好主意,因此如果需要,您可以使用单个 rescue 子句捕获它们.) 或者如果你使用错误代码,它看起来像:

    urls.map do |url|
      response = crawl(url)
      if [:page_not_found, :unauthorized].include? response
        ""
      else
        response
      end
    end
    

    你觉得哪个更好读?这真的取决于你。您想做的一件事是使用整数常量来处理错误。为什么使用整数?当您在调试跟踪中打印它们时,您必须查看常量列表以了解每个常量的含义。使用符号在计算上同样有效。

    【讨论】:

      【解决方案3】:

      您应该提出错误。如果您遇到格式错误的 URL,或者找不到该页面,或者您无权访问该页面,则意味着 您无法继续抓取。引发错误或异常从方法返回,并让调用者处理异常情况。

      它还应包括有关错误的信息,例如错误代码、导致错误的 URL 以及任何其他相关信息。它可以帮助决定如何最好地处理错误,并且可以在以后格式化为对用户有用的消息。

      永远不应该做的事情是返回数字错误代码。 Ruby 不是 C。只需使用符号即可。

      【讨论】:

        【解决方案4】:

        为什么不会你抛出异常?除了类型之外,它们还可以封装其他信息,被简单地拯救,如果您使用的是 IDE,它们是一等公民。

        【讨论】:

        • 我认为你是否使用 IDE 不应该被考虑在你的编程如何what中。
        • @AndrewMarshall 这是一种观点,但实际上很重要。
        • 除了可能的风格/文档约定(实际上不会影响文字代码),不,不应该。无论您是否使用 IDE,作为一等公民的例外(我假设您的意思是类)都是正确的——使用 IDE 不会以某种方式改变这一点。
        • @AndrewMarshall 没说有。然而,它确实改变了开发环境可以为您围绕符号/类/等做的事情。你正在使用。随意关注我所说的这一方面,尽管它是相当辅助的,但没有我你将不得不这样做。继续!
        【解决方案5】:

        如果是异常,那么一定会引发异常!在我看来,这三个案例都是例外。虽然有些人可能会争辩说 4xx 状态码不值得例外,因为您可能期望它们会发生,但它们仍然是客户端错误

        您还可以阅读有关 Ruby 的 throw/catch 的信息,它为“不要将异常用于控制流”的情况提供了类似异常的行为(尽管我认为这里不是这种情况)。

        【讨论】:

          猜你喜欢
          • 2012-10-16
          • 1970-01-01
          • 1970-01-01
          • 2020-12-29
          • 2017-03-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-07-06
          相关资源
          最近更新 更多