合同设计对您有用吗?

合同设计对您有用吗?

Does Design By Contract Work For You?

您是否专业使用按合同设计? 是从项目开始就要做的事情,还是可以改变方式并开始将其纳入软件开发生命周期中? 您发现设计方法的利弊是什么?

我在一个研究生学校课程中遇到了"按合同设计"方法。 在学术环境中,这似乎是一种非常有用的技术。 但是我目前还没有专业地使用"按合同设计",也不知道其他正在使用它的开发人员。 最好能从SO人群那里得知其实际使用情况。


我不能高度推荐它。如果您有一个采用内联文档合同规范的套件,那就特别好,例如:

1
2
3
4
// @returns null iff x = 0
public foo(int x) {
  ...
}

并将它们转换为生成的单元测试,如下所示:

1
2
3
public test_foo_returns_null_iff_x_equals_0() {
  assertNull foo(0);
}

这样,您实际上可以看到正在运行的测试,但是它们是自动生成的。顺便说一下,不应将生成的测试签入源代码管理。


当您在必须相互通信的应用程序之间建立接口时,您真的会真正欣赏按合同设计。

没有合同,这种情况很快就变成了责备网球运动。团队不断来回敲控指控,浪费了大量时间。

有了合同,责任就显而易见了。

来电者是否满足前提条件?如果不是,客户团队需要修复它。

给定有效请求,接收方是否满足发布条件?如果不是,服务器团队需要修复该问题。

双方都遵守合同,但结果不令人满意吗?合同不足,需要升级该问题。

为此,您不需要以断言的形式实施合同,您只需要确保各方都记录了文件并达成协议即可。


弗兰克·克鲁格写道:

Gaius: A Null Pointer exception gets thrown for you automatically by the runtime, there is no benefit to testing that stuff in the function prologue.

我对此有两个回应:

  • 空只是一个例子。对于square(x),我想测试结果的平方根(大约)是参数的值。对于设置员,我想测试该值实际是否已更改。对于原子操作,我想检查所有组件操作是否成功或全部失败(真的是一次测试成功,而n次测试失败)。对于弱类型语言的工厂方法,我想检查是否返回了正确的对象。清单一直在继续。基本上,可以在一行代码中进行测试的任何东西都是序言注释中代码合同的很好的候选人。

  • 我不同意您不应该测试事物,因为它们会生成运行时异常。如果有的话,您应该测试可能会生成运行时异常的事物。我喜欢运行时异常,因为它们会使系统快速故障,这有助于调试。但是示例中的null是某些可能输入的结果值。有一个关于永不返回null的论点,但如果要这样做,则应该对其进行测试。


  • 如果您研究STL,boost,MFC,ATL和许多开源项目,则可以看到有太多ASSERTION语句,这使项目更安全地进行。

    按合同设计!它确实适用于实际产品。


    在SOA领域中执行任何操作时不按合同进行设计是绝对愚蠢的,如果您正在从事任何模块化工作,这在以后可能会被一点一点地换掉,尤其是在涉及到任何黑盒子的情况下,这总是有帮助的。


    我发现这表明Go编程语言没有使按合同设计成为可能的结构。 panic / defer / recover并不完全一样,因为defer和recovery逻辑可以忽略恐慌,而IOW可以忽略违约。至少需要某种形式的无法恢复的恐慌,这确实可以断言。或者,最好是通过合同结构(前提条件和后置条件,实现和类不变式)直接为设计提供语言支持。但是,鉴于语言纯粹主义者在Go船长的领导下坚毅不拔,因此我所做的任何改动都不会改变。

    通过在恐慌函数的最后一个defer函数中检查特殊的断言错误并调用runtime.Breakpoint()在恢复期间转储堆栈,可以实现类似断言的行为。要断言,行为必须是有条件的。当然,当在执行断言之后添加新的延迟函数时,这种方法就会瓦解。在大型项目中,这恰恰在错误的时间发生,从而导致错误遗漏。

    我的观点是,断言在很多方面都非常有用,以至于不得不跳动它可能会让人头疼。


    根据我的经验,单元测试和按合同设计都是有价值的测试方法。

    我曾尝试在系统自动测试框架中使用按合同设计,而我的经验是,这种灵活性和灵活性不易通过单元测试获得。例如,可以运行更长的序列并验证
    每次执行动作时,响应时间都在限制范围内。

    查看InfoQ上的演示,看来按合同设计是组件集成阶段中常规单元测试的宝贵补充。
    例如,可以先创建一个模拟接口,然后再使用该组件-
    或发布新版本的组件时。

    我没有找到可以满足通过合同测试进行设计的所有设计要求的工具包
    在.Net / Microsoft平台中。


    代替更具表现力的字体系统,我绝对会在军事级项目中按合同使用设计。

    对于弱类型语言或具有动态作用域的语言(PHP,JavaScript),功能协定也非常方便。

    对于其他所有方面,我都会抛弃对Beta测试人员和单元测试的依赖。

    Gaius:运行时会自动为您抛出Null Pointer异常,因此测试函数序言中的内容没有任何好处。如果您对文档更感兴趣,那么我将使用可与静态分析器等配合使用的注释(例如,确保代码不会破坏您的注释)。

    更强大的类型系统与按合同设计相结合似乎是必经之路。以Spec#为例:

    The Spec# programming language. Spec#
    is an extension of the object-oriented
    language C#. It extends the type
    system to include non-null types and
    checked exceptions. It provides
    method contracts in the form of pre-
    and postconditions as well as object
    invariants.


    是的,它确实!实际上,几年前,我设计了一个用于参数验证的小框架。我当时在做一个SOA项目,其中不同的后端系统进行了各种验证和检查。但是为了增加响应时间(在输入无效的情况下,并减少加载这些后端系统的时间),我们开始验证所提供服务的输入参数。不仅适用于Not Null,而且适用于String模式。或集合中的值。以及参数之间具有依赖性的情况。

    现在我意识到我们当时通过合同框架实施了一个小设计:)

    对于那些对小型Java参数验证框架感兴趣的人,这里是链接。这是作为纯Java解决方案实现的。


    我实际上并不是每天都使用"按合同设计"。但是,我的确知道它已经被纳入D语言中,作为D语言的一部分。


    推荐阅读