5

我对这种Design by Contract方法很感兴趣。似乎preconditions必须使用已检查的异常来强制执行它们。
但是对于post-conditionsclass-invariants我认为这assertions是首选。
我对吗?如果我是正确的,为什么允许 forpost-conditionsclass-invariants可能被禁用的断言?不应该强制执行后置条件和不变量吗?

4

3 回答 3

8

组件上的后置条件和类不变量只有在组件本身编写不正确时才会失败。单元测试应该捕获所有这些。当然,在生产中实际检查它们是允许的,但这并不一定值得在性能上进行权衡。

另一方面,如果该组件的用户不正确,前置条件可能会失败。对组件本身的测试无法检查这些,因此有必要更积极地失败,以便那些单元测试失败。

于 2012-12-25T18:37:20.177 回答
3

根据定义,违反先决条件是编程错误。因此,最不幸的是用检查异常来表示这种违规行为。因为正确的代码将被迫显式地捕获一个肯定永远不会抛出的异常,并将其作为未经检查的异常重新抛出,以便可以检测到编程错误。

于 2013-08-09T21:17:41.223 回答
0

你不应该区别对待它们——它们都应该是断言。

OP 说:

...似乎必须使用先决条件检查异常来强制执行它们。...为什么允许可能被禁用的后置条件和类不变量断言?不应该强制执行后置条件和不变量吗?

您似乎建议前置条件、后置条件和类不变量应该始终打开并始终由服务(方法/被调用者)检查。如果我们谈论的是源自 Bertrand Meyer的按合同设计(DBC),那么情况并非如此。Meyers 认为,从生产代码的角度来看,这些条件应该只在一个地方由客户端(调用者)或服务(被调用者)确保——这是客户端和服务之间的合同。相比之下,防御性编程说您应该在两个地方都对检查进行编码(Meyers 认为这很浪费并增加了不必要的复杂性)。

DBC 的合同部分是规范将明确谁负责什么:如果客户端将确保前置条件(并且服务可以假设它们为真),那么服务将确保后置条件(以及调用者可以假设它们是真的)。迈耶斯当然明白,服务为了测试和调试目的检查前置条件/​​后置条件/不变量是明智的(以确保系统在测试/调试期间快速失败),这就是为什么断言这些条件是明智的并在测试/调试期间启用断言,但目的是可以禁用或删除这些检查以进行生产。

例如,如果您设计了一个堆栈,使得调用者有责任在调用 pop()(前提条件)之前检查堆栈是否为空,那么您也不应该将 pop() 方法作为生产代码的一部分进行编码检查堆栈是否为空,也不应该将已检查的异常作为处理条件的方法签名的一部分。在那里有前置条件断言来帮助验证和调试是可以的,但目的是一旦开发和测试完成(代码可能没有错误),生产代码将只在调用者确保前置条件的情况下运行,而不是被调用者(如果这是您为 API 设计合同的方式)。

如果您有一个已检查的异常作为 pop() 方法的一部分,并且您在 pop() 方法中检查堆栈是否为空,作为始终在线的一部分,必须处理,生产代码,那么您说的是服务(被调用者)负责检查,如果堆栈为空,它承诺抛出异常——它现在是后置条件的一部分。在这种情况下,调用者不需要明确地检查它——他们应该只处理异常。否则,如果调用者和被调用者都先检查堆栈是否为空,则不是合同设计。

最后一点,有些人认为这意味着 Meyer 说调用者应该始终负责检查堆栈是否为空或参数是否为空等。不是这样 - Meyers 只是说你必须清楚规范前置条件和后置条件,以便正确编码客户端和服务。作为 API 设计者和实施者,您决定前置条件和后置条件中的内容。如果您可以合理地确定客户端(调用者)将确保前提条件(例如,因为它是一个内部类,并且您可以控制调用服务/方法的任何地方),那么将“堆栈非空”或“ parameter-a-is-not-null”部分的前提条件,并将责任放在客户端。但是,如果您认为这不是一个合理的假设(例如

[[对于类似咆哮的回答,我很抱歉——我是 Bertrand Meyer 和 Design-by-Contract 的忠实粉丝。]]

于 2013-04-07T13:30:49.740 回答