9

契约式设计编程的最佳实践是什么。

在大学时,我通过合同范式学习设计(在 OO 环境中)我们已经学习了三种解决问题的方法:

1)总编程:涵盖所有可能的例外情况(参见数学)

2)名义规划:只有在满足前提条件时才“承诺”正确的效果。(否则效果未定义)

3)防御性编程:使用异常来表示方法的非法调用

现在,我们已经专注于不同的 OO 场景在每种情况下的正确使用,但我们还没有学会何时使用 WHICH……(主要是练习强制执行的策略……)

现在我觉得我没有问过我的老师很奇怪(但话说回来,在上课时,没有人问过)

就个人而言,我现在从不使用名义,并且倾向于用异常替换前置条件(所以我宁愿使用 : throws IllegalDivisionByZero,而不是说明'前置条件:除法器应该不同于零)并且只编程有意义的总和(所以我不会返回除以零的常规值),但这种方法只是基于个人的发现和喜欢。

所以我问你们:

有没有最好的做法??

4

5 回答 5

6

我不知道这个部门,它并不能真正反映我的经验。

完全编程几乎是不可能的。您不能保证涵盖所有例外情况。所以基本上你应该限制你的范围并拒绝超出范围的情况(这是Pre-conditions的作用)

不需要名义编程。应禁止未定义的效果。

防御性编程是必须的。您应该始终表示非法调用方法。

我赞成实施完整的按合同设计元素,在我看来,这是全面编程的实用且实惠的版本

表示方法非法调用的前提条件(一种防御性编程)。尽量限制您的范围,以便您可以简化代码。如果可能,通过缩小范围来避免复杂的实现。

如果未获得所需效果,则引发错误的后置条件。即使是你的错,你也应该通知来电者你错过了目标。

检查对象一致性是否保留的不变量。

于 2009-09-19T22:35:19.840 回答
1

这一切都归结为您希望分配给客户和合同实施者的责任。

在防御性编程中,您强制实施者检查错误情况,这在某些情况下可能代价高昂甚至是不可能的。想象一下由 binarySearch 指定的合约,例如您的输入数组必须进行排序。您在运行算法时无法检测到这一点。您必须对其进行手动检查,这实际上会将执行时间提高一个数量级。支持我的观点是javadocs中方法的签名。

另一点是人们和框架现在倾向于实现异常翻译机制,主要用于将检查的异常(防御风格)翻译为运行时异常,如果发生错误,就会弹出。这样,合同的客户和实施者在彼此打交道时就不必担心了。

同样,这是我的个人观点,仅以我有限的经验为依据,我很想听到更多关于这个主题的信息。

于 2009-04-13T20:11:35.357 回答
1

...但我们还没有学会何时使用 WHICH...

我认为最好的做法是“尽可能防御”。如果可以,请进行运行时检查。正如@MahdeTo 有时提到的那样,出于性能原因这是不可能的;在这种情况下,依赖于未定义或不令人满意的行为。

也就是说,在您的文档中明确说明哪些有运行时检查,哪些没有。

于 2009-04-13T20:22:34.613 回答
0

像许多计算一样,“它取决于”可能是最好的答案。

通过明确记录功能的条件,合同设计/合同编程可以极大地帮助开发。仅文档就可以提供帮助,甚至无需将其制成(编译)代码。

在可行的情况下,我建议采取防御措施——检查每一个条件。但仅用于开发和调试版本。通过这种方式,当条件被破坏时,大多数无效假设都会被捕获。一个好的构建系统将允许您在模块或文件级别以及全局上打开和关闭不同的条件类型。

然后,在软件的发布版本中采取的行动取决于系统以及触发条件的方式(通常区分外部接口和内部接口)。发布版本可能是“完全编程”——所有条件都会给出定义的结果(可能包括错误或 NaN)

对我来说,“名义编程”在现实世界中是一条死胡同。你假设如果你传递了正确的值(当然你做了),那么你收到的值是好的。如果你的假设是错误的——你就会崩溃。

于 2009-04-15T12:53:40.510 回答
0

我认为测试驱动编程就是答案。在实际实现模块之前,您首先创建一个单元测试(称之为合同)。然后逐步实现该功能并确保合同在您进行时仍然有效。通常我从简单的存根和模型开始,然后逐渐填充其余部分,用真实的东西代替刺。不断改进,让测试更强大。最后,您最终获得了所述模块的强大实现,并且您拥有了一个出色的测试平台——合约的编码实现。后来,如果有人修改了模块,首先你看看它是否仍然适合测试台。如果没有,则合同已失效 - 拒绝更改。或者,合同已过时, - 修复单元测试。等等..无聊的软件开发周期:)

于 2009-04-18T19:23:53.010 回答