-->

通过使用断言或例外合约设计? [关闭](Design by contract using ass

2019-06-17 14:17发布

当合同编写一个函数或方法首先检查它的前提条件是否满足,开始在其职责工作,对前? 两个最重要的方式做这些检查是由assertexception

  1. 断言失败只在调试模式。 为了确保它是(单位)测试的所有单独的合同的前提条件至关重要,看看他们是否真的失败。
  2. 例外调试和发布模式中失败。 这有测试调试行为与释放行为的好处,但它会带来运行时性能损失。

你觉得哪一个最好?

请参阅相关型号的问题在这里

Answer 1:

禁用断言在发布版本就像是说“我永远不会有任何问题,任何一个发布版本”,这往往并非如此。 因此,断言不应该在一个发布版本被禁用。 但你不想要的发行版本崩溃时发生错误要么,你呢?

因此,使用异常和用好。 使用一个良好的,坚实的异常层次结构,并确保你赶上,你可以把异常抛出钩子在调试器来捕捉它,并在释放模式可以弥补的错误,而不是直线上升的崩溃。 这是走的更安全的方式。



Answer 2:

经验法则是,试图抓住别人的错误时,你应该使用,当你试图抓住自己的错误的断言和异常。 换句话说,你应该使用异常检查先决条件的公共API函数,每当你来说是外部系统的任何数据。 您应该使用断言的是内部到系统的功能或数据。



Answer 3:

我遵循的原则是这样的:如果这种情况可以通过编码然后使用断言得到切实避免。 否则,使用异常。

断言是确保合同的遵守。 合同必须是公平的,所以客户端必须是在一个位置,以确保它符合。 例如,你可以在一个URL必须是有效的,因为什么是和规则没有已知有效的URL和一致的合同状态。

例外的是对于那些在客户端和服务器的控制范围之外的情况。 例外意味着出了问题,并没有什么,可能已经完成,以避免它。 例如,网络连通性的应用控制之外所以没有什么可以做,以避免网络错误。

我想补充一点,断言/异常的区别是不是真的想它的最佳方式。 你真的想在思考什么是合同以及如何执行。 在我的网址上面的例子做的最好的事情是有一个封装URL是null或有效的URL的类。 这是一个字符串转化为一个能够执行合同的URL,如果无效则抛出异常。 与URL参数的方法更清晰,与一个字符串参数和指定URL断言的方法。



Answer 4:

声称是捕捉一些开发者已经做了错误(不只是自己 - 另一位开发人员在你的团队也)。 如果它是合理的,用户不慎就可能创造这个条件,那么它应该是一个例外。

同样想想后果。 断言通常会关闭应用程序。 如果有任何现实的期望,条件可以从回收,你应该使用异常。

在另一方面,如果这个问题只能是由于程序员的错误,然后使用断言,因为你想尽快知道。 一个例外可能会被捕获并处理,而你永远不会了解它。 是的,你应该禁用断言在发布代码,因为有你想要的应用程序来恢复,如果有它可能丝毫的机会。 即使你的程序的状态被打破深刻的用户也许会是能够保存他们的工作。



Answer 5:

这是不完全真实的“断言仅在调试模式中失败。”

面向对象的软件建设,第二版由伯特兰·迈耶,笔者留下了在释放模式检查的前提条件的门。 在这种情况下,当断言失败会发生什么是...断言违反异常升高! 在这种情况下,没有从复苏态势:有用的东西可以做虽然,它是自动生成错误报告,并在某些情况下,重新启动应用程序。

这背后的动机是先决条件通常价格比不变量和后置条件进行测试,并在发布版本,在某些情况下,正确性和“安全”是比速度更重要。 即对于许多应用程序的速度是不是一个问题,但稳健性 (该方案在一个安全的方式行事时,其行为是不正确的能力,即当合同被打破)是。

如果你总是把先决条件检查启用? 这取决于。 由你决定。 没有统一的答案。 如果你正在做软件银行,可能会更好中断执行与惊人的消息,而不是转移$ 1,000,000个,而不是$ 1,000。 但是,如果你正在编写一个游戏? 也许你需要的一切,你可以得到的速度,如果有人得到1000分,而不是10,因为前提没赶上(因为他们没有启用)中的一个错误,倒霉。

在这两种情况下,你最好有钓到测试过程中的错误,你应该做的启用断言你的测试的显著部分。 这里正在讨论的是什么,是对那些在其先决条件在生产代码失败,只是不由于不完整的测试更早检测出场景中罕见的情况下最好的策略。

总之, 你可以断言,仍然自动获得例外 ,如果你让他们启用了-至少在埃菲尔铁塔。 我认为做在C ++一样,你需要自己输入。

另见: 什么时候应该断言留在生产代码?



Answer 6:

有一个巨大的线程就断言的启用/禁用在发布版本上comp.lang.c ++。缓和,而如果你有几个星期,你可以看到在这个意见的差异程度。 :)

相反, coppro ,我相信,如果你不知道某个断言可以在发布版本被禁用,那么它不应该已经断言。 断言是防止被打破计划不变。 在这种情况下,只要你的代码的客户担心会出现两种可能的结果之一:

  1. 冷冲模具某种OS类型的故障,导致呼叫中止。 (如果没有断言)
  2. 通过直接调用中止死。 (附声明)

有用户没有区别,但是,它有可能是在断言是存在于绝大多数运行其中的代码不会失败的代码添加不必要的性能开销。

该问题的答案其实取决于谁的API的客户会更多。 如果你正在编写提供API库,那么你需要某种形式的机制来通知你的客户,他们错误地使用API​​。 除非您提供的库的两个版本(一个断言,一个没有)则断言是不太合适的选择。

就我个人而言,我不知道我会与这种情况下的例外会有两种。 例外的是更适合,其中恢复的适当形式可以发生。 例如,它可能是你要分配内存。 当你发现一个“的std :: bad_alloc的”异常有可能以释放内存,然后再试一次。



Answer 7:

概述了我对此事的看法状态这里: 你如何验证对象的内部状态? 。 一般情况下,断言您的索赔被别人扔的冲突。 禁用断言在发布版本,你可以这样做:

  • 禁用断言昂贵的检查(如检查的范围是否订购)
  • 保持启用琐碎的检查(如检查一个空指针或一个布尔值)

当然,在发布版本,未能断言和捕获的异常应处理的另一种方式比在调试版本(它可能只是调用的std ::终止)。 写日志错误的地方(可能到一个文件中),告诉大家,发生内部错误的客户。 客户将能够为您发送日志文件。



Answer 8:

你问有关设计时和运行时错误之间的区别。

断言是“哎程序员,这是破”的通知,他们是在提醒你,当他们发生了,你不会注意到的错误。

例外的是“嗨用户,有些事情出了问题”的通知(很明显,你可以编写赶上他们,使用户永远不会告诉),但这些设计时,乔用户正在使用的应用程序在运行时发生。

所以,如果你认为你可以得到你所有的虫子,只使用异常。 如果你认为你不能.....使用异常。 您仍然可以使用调试断言作出例外少,当然数量。

不要忘记,许多前提条件的将用户提供的数据,所以你需要告知用户的一个很好的方式他的数据是没有好。 要做到这一点,你会经常需要返回错误数据下降调用堆栈,他与交互的位。 断言不会有用,然后 - 所以,如果您的应用程序是n层的伤口上撒盐。

最后,我既不使用 - 错误代码是远远优于你认为会经常发生错误。 :)



Answer 9:

我更喜欢第二个。 虽然你的测试可能已经运行良好, 墨菲说,意想不到的事情会出问题。 所以,与其在实际的错误方法调用得到一个例外,你最终描绘出一个NullPointerException(或同等学历)10帧堆栈更深。



Answer 10:

以前的答案是正确的:使用公共API函数例外。 你可能希望服从这个规则,唯一的一次,当检查的计算成本高昂。 在这种情况下,你可以把它放在一个断言。

如果您认为违规的前提条件是有可能的,把它作为一个例外,或者重构的前提了。



Answer 11:

你应该同时使用。 声称是为了方便您作为一个开发者。 异常捕获到你错过或运行时没有想到的。

我已经长大了喜欢油腔滑调的错误报告功能 ,而不是普通的旧的断言。 他们表现得像断言语句但不是停止程序,他们只返回一个值,让程序继续进行。 它的工作原理出奇地好,并且作为奖励,你能看到发生了什么,以你的程序时,函数不返回“什么是应该”的其余部分。 如果崩溃,你知道你的错误检查不严其他地方的道路。

在我的最后一个项目,我用的功能,这些样式来实现的先决条件检查,如果他们中的一个失败了,我会打印一个堆栈跟踪到日志文件,但继续运行。 救了我吨的调试时间,当我运行调试版本,当其他人会遇到的问题。

#ifdef DEBUG
#define RETURN_IF_FAIL(expr)      do {                      \
 if (!(expr))                                           \
 {                                                      \
     fprintf(stderr,                                        \
        "file %s: line %d (%s): precondition `%s' failed.", \
        __FILE__,                                           \
        __LINE__,                                           \
        __PRETTY_FUNCTION__,                                \
        #expr);                                             \
     ::print_stack_trace(2);                                \
     return;                                                \
 };               } while(0)
#define RETURN_VAL_IF_FAIL(expr, val)  do {                         \
 if (!(expr))                                                   \
 {                                                              \
    fprintf(stderr,                                             \
        "file %s: line %d (%s): precondition `%s' failed.",     \
        __FILE__,                                               \
        __LINE__,                                               \
        __PRETTY_FUNCTION__,                                    \
        #expr);                                                 \
     ::print_stack_trace(2);                                    \
     return val;                                                \
 };               } while(0)
#else
#define RETURN_IF_FAIL(expr)
#define RETURN_VAL_IF_FAIL(expr, val)
#endif

如果我需要运行时的参数检查,我应该这样做:

char *doSomething(char *ptr)
{
    RETURN_VAL_IF_FAIL(ptr != NULL, NULL);  // same as assert(ptr != NULL), but returns NULL if it fails.
                                            // Goes away when debug off.

    if( ptr != NULL )
    {
       ...
    }

    return ptr;
}


Answer 12:

我试过几个合成其他的答案在这里用我自己的看法。

使用断言在您想在生产中禁用的情况下,向使他们在犯错,唯一的真正原因在生产中禁用,但不是在发展,是加快程序。 在大多数情况下,这种加速会不会显著,但有时代码是关键的时间或测试的计算成本高昂。 如果代码是关键任务,则可以例外,尽管放缓是最好的。

如果有任何复苏的真正的机会,使用异常的断言并非设计为从恢复。 例如,代码很少被设计为从编程错误中恢复,但它的设计来自因素,如网络故障或锁定的文件进行恢复。 错误不应该作为例外处理只是为是程序员的控制范围之外。 相反,这些错误的预测,相对于编码错误,使他们更可亲恢复。

重新说法,更容易调试断言:从正确命名的异常堆栈跟踪是一样容易读作断言。 良好的代码应该只捕获特定类型的异常,所以异常不应该被忽视,因为被抓。 但是,我认为Java有时迫使你捕获所有异常。



Answer 13:

又见这个问题 :

建设发布的时候我有些情况下,断言被禁用。 您可能没有在这个控制(否则,你可以建立与断言),所以它可能是一个好主意,像这样做。

以“纠正”输入值的问题是,来电者不会得到他们所期望的,这可能导致程序完全不同的部分,使得调试一场噩梦问题,甚至崩溃。

我通常在if语句抛出一个异常接管断言的作用的情况下,它们将被禁止

 assert(value>0); if(value<=0) throw new ArgumentOutOfRangeException("value"); //do stuff 


Answer 14:

经验法则,对我来说,就是利用断言表达式来查找内部错误和异常的外部错误。 您可以通过从格雷格受益于下面的讨论多在这里 。

断言表达式用于发现程序错误:在程序的逻辑本身错误或在其相应的执行错误。 断言条件验证程序保持在定义的状态。 A“定义的状态”基本上是一个与程序的假设一致。 请注意,“定义的状态”程序不必是一个“理想状态”,甚至“通常状态”,甚至“可使用状态”,而更对重要的一点以后。

要了解断言如何融入一个程序,在C ++程序,它是关于取消引用指针考虑一个程序。 现在应该例行测试指针是否为NULL解除引用之前,还是应该断言指针不为空,然后继续前进,取消对它的引用而不管?

我想,大多数开发商会想两者都做,添加断言,还要检查空值指针,为了不崩溃应该断言的情况失败。 从表面上看,既进行了测试和检查可能似乎是最明智的决定

不同于其声称的条件,程序的错误处理(例外)指的不是程序中的错误,但要输入程序从环境中获得。 这些通常是在某人的一部分“错误”,比如尝试登录到一个帐户没有密码键入用户。 而且即使错误可能会阻止程序的任务的圆满完成,不存在程序故障。 该程序无法登录没有密码的用户由于外部错误 - 对用户的部分错误。 如果情况是不同的,并以正确的密码和程序输入的用户没有认识到它; 话虽然结果仍然是一样的,现在的故障都属于程序。

错误处理(例外)的目的是双重的。 首先是传送给用户(或一些其它客户端),在程序的输入的误差已被检测到和这意味着什么。 第二个目的是恢复被检测到错误后的应用程序,以明确限定的状态。 需要注意的是该程序本身不是错误在这种情况下。 诚然,该程序可在非理想状态,甚至是一个状态,其中可以做什么有用的,但没有编程errorl。 相反,因为错误恢复状态是一个由程序的设计预期,但国际空间站的一个程序可以处理。

PS:你可能要检查出类似的问题: 异常Vs的断言 。



文章来源: Design by contract using assertions or exceptions? [closed]