18

这些天来,我习惯于检查每个功能的每个前提条件,因为我从大学的操作系统编程课程中养成了习惯。

另一方面,在软件工程课程中,我们被教导说,一个共同的前提条件应该只检查一次,例如,如果一个函数委托给另一个函数,第一个函数应该检查它们,但在第二个函数中再次检查它们是多余的。

我确实看到了冗余点,但我当然觉得总是检查它们更安全,而且你不必跟踪它们以前检查的位置。

这里的最佳做法是什么?

4

9 回答 9

11

我没有看到关于如何检查先决条件的“硬性规定”,但我通常将其视为方法文档。如果它是公共范围的,我断言满足先决条件。这背后的逻辑是,范围表明您期望消费范围更广,影响力更小。

就个人而言,围绕私有方法进行断言的努力是我为“关键任务”方法保留的,这些方法基本上是执行关键任务、受外部合规要求约束或在发生事件时不可恢复的方法。例外。这些主要是“判断电话”。

节省的时间可以重新投入到彻底的单元和集成测试增强中,以尝试消除这些问题,并放置工具来帮助执行输入断言的质量,因为它会被客户端消耗,无论它是你的控制与否。

于 2010-05-28T11:25:42.500 回答
5

我认为这取决于团队的组织方式:检查来自团队外部的输入。

  • 检查来自最终用户的输入
  • 检查其他团队编写的软件组件的输入
  • 信任从您自己的组件/您自己的团队中收到的输入。

这样做的原因是为了支持和维护(即错误修复):当有错误报告时,您希望能够尽快知道哪个组件出现故障,即将错误分配给哪个团队。

于 2010-05-28T11:30:30.507 回答
4

如果一个函数委托给另一个函数,第一个函数应该检查它们,但在第二个函数中再次检查它们是多余的。

如果你改变这些函数相互调用的方式会怎样?或者你在第二个函数中引入了新的验证要求,第一个函数委托给?我会说总是检查它们更安全。

于 2010-05-28T11:32:23.560 回答
4

我养成了区分检查断言先决条件的习惯,这取决于(正如人们在评论中指出的那样)调用是来自外部(未经检查的异常,可能发生)还是内部(断言,不应该发生) )。

理想情况下,生产系统不会受到断言的惩罚,您甚至可以使用类似 Design By Contract(TM) 的机制来静态释放断言。

于 2010-08-07T07:23:43.257 回答
3

根据我的经验,这取决于您的封装。如果内部函数是私有的,那么您可以非常确定它的前提条件已设置。

猜猜这都是关于德墨忒耳法则的,只和你的朋友聊天等等。

作为最佳实践的基础,如果电话是公开的,您应该检查您的输入。

于 2010-05-28T11:23:27.967 回答
2

我认为最好的做法是仅在它们有一天会失败时才进行这些检查。如果您执行以下操作会有所帮助。

调试

当一个模块中的多个私有函数(只有一个维护者)交换数据时,检查它们是没有意义的。当然,也有例外,特别是如果您的语言没有静态类型系统,或者您的数据是“字符串类型”。

但是,如果您公开公共 API,总有一天,有人会失败您的先决条件。维护呼叫模块的人离您越远(在组织结构和物理位置),它发生的可能性就越大。当它发生时,一个明确的前置条件失败声明,以及它发生的规范,可以节省数小时的调试时间。 泄漏抽象定律仍然适用……

质量保证

前置条件失败有助于 QA 调试他们的测试。如果模块的单元测试导致模块产生前置条件失败,则意味着测试不正确,而不是您的代码。(或者,您的前提条件检查不正确,但这不太可能)。

如果执行 QA 的方法之一是静态分析,那么前提条件检查(如果它们具有特定符号)(例如,只有这些检查使用assert_precondition宏)也会有所帮助。在静态分析中,区分不正确的输入和源代码错误非常重要。

文档

如果您没有太多时间来创建文档,您可以让您的代码辅助伴随它的文本。清晰可见的前置条件检查,被认为与实现的其余部分是分开的,在某种程度上“记录”了可能的输入。(以这种方式记录代码的另一种方法是编写单元测试)。

于 2010-05-28T11:38:06.713 回答
2

与所有事情一样,评估您的要求以找到适合每种情况的最佳解决方案。

当先决条件更容易检查时(“指针不为空”),您不妨经常这样做。难以检查的前提条件(“指向一个有效的以空字符结尾的字符串”)或者在时间、内存或其他资源方面很昂贵的前提条件可能会以不同的方式处理。使用帕累托原则,收集低垂的果实。

// C, C++:
void example(char const* s) {
  // precondition: s points to a valid null-terminated string
  assert(s); // tests that s is non-null, which is required for it to point to
  // a valid null-terminated string.  the real test is nearly impossible from
  // within this function
}

保证先决条件是调用者的责任。正因为如此,一些语言提供了一个可以选择跳过的“断言”结构(例如,为 C/C++ 定义 NDEBUG,为 Python 定义命令行开关),以便您可以在特殊构建中更广泛地测试先决条件,而不会影响最终性能。然而,如何使用断言可能是一场激烈的争论——再次,弄清楚你的需求并保持一致。

于 2010-05-28T14:09:30.097 回答
2

这是一个有点老的问题,但不,前提条件不必每次都检查。这真的取决于。

例如,如果您对向量进行二进制搜索怎么办。前提是已排序的向量。现在,如果您每次检查向量是否已排序,这需要线性时间(对于每个向量),因此效率不高。客户必须了解前提条件并确保满足它。

于 2013-06-18T18:10:21.943 回答
0

最佳做法是始终检查它们。

于 2010-05-28T11:23:55.530 回答