引发自定义异常,返回常量或返回符号?为什么?

问题描述:

假设我有一个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 

这是最好的?或者它取决于什么?

对于指示程序员错误的东西,例如传递给方法的参数类型错误,肯定会抛出异常。这个异常会导致程序崩溃,引起程序员的注意,他们错误地使用了你的类,这样他们就可以解决这个问题。在这种情况下,返回错误代码是没有意义的,因为程序将不得不包含代码来检查返回值,但在程序调试完成后,这种错误不应该发生。

在您的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 

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

如果这是一个例外,那么通过一切手段引发异常!在我看来,所有这三种情况都是例外情况。虽然有些人可能会争辩说4xx状态码并不值得您考虑,但它们仍然是客户端错误

您也可以阅读关于Ruby的throw/catch,它们适用于“不使用控制流的异常”的情况下的类似于异常的行为(尽管我认为这不是这种情况)。

为什么不会你抛出异常?除了类型之外,它们可以封装额外的信息,可以轻松获得救援,如果您使用的是IDE,则是一等公民。

+5

我不认为你是否使用IDE或者不应该考虑* how *或*你编程的内容。 – 2012-03-04 20:47:16

+2

@AndrewMarshall这是一个观点,但它确实很重要。 – 2012-03-04 20:52:27

+3

除了可能的风格/文档约定(实际上并不影响文字代码),否则不应该。作为头等公民的例外情况(我认为你的意思是课程)是真实的,不管你是否使用IDE--使用IDE都不会改变这种情况。 – 2012-03-04 20:59:50

您应该提出错误。如果您遇到格式不正确的网址,或者找不到网页,或者您无权访问该网页,则表示您无法继续抓取。从方法中返回错误或异常,并让调用者处理异常情况。

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

不应该做什么,永远,是返回数字错误代码。 Ruby不是C.只需使用符号。

我反对在网络上遇到403s,404s,畸形的网址和类似的常见事件时使用例外。例外是指“内部”错误。在World Wild Web中,糟糕的网址完全没有意义。应该有一个方法来处理每个不同的URL疾病。我会亲自将特殊值作为符号返回,或者记录发生了什么的一些“SpecialCase”对象。还有未被使用的catch ... throw语句。

+1

仅仅因为错误发生了很多,并不意味着它不是一个例外。如果汽车由于电池电量不足而无法启动,那么这是否是正常行为,但是这是一个错误情况,需要特殊情况才能解决。不要让你的坏习惯与最佳实践混淆。无论通信发生的频率如何,通信失败都是例外情况。失败!=成功。 – ocodo 2013-06-06 03:07:24

+0

我的答案并不明显,我的理由并不明显,所以也许它并不是那么好:更糟糕的是,我自己也忘了他们:-)但是我的习惯不是怪,因为我喜欢例外 - 不仅仅是当电池是平的,而且路灯是红色的时候。有[关于这个的一个讨论](http:// *。com/questions/2018137),明确提到Sinatra使用catch/throw来处理HTTP异常,[我开始另一个](http://*.com/questions/16972757)。 catch/throw是否真的很糟糕,或者只是没有被使用? – 2013-06-06 21:50:52