您专业使用合同设计吗?这是您从项目一开始就必须做的事情,还是您可以改变方向并开始将其纳入您的软件开发生命周期中?您发现该设计方法的优点/缺点是什么?

我遇到了 合同设计 研究生课程中的方法。在学术环境中,这似乎是一种非常有用的技术。但我目前并不专业地使用合同设计,而且我不知道有其他开发人员在使用它。很高兴能听到 SO 人群的实际使用情况。

有帮助吗?

解决方案

我极力推荐它。如果您有一个采用内联文档合同规范的套件,那就特别好,如下所示:

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

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

public test_foo_returns_null_iff_x_equals_0() {
  assertNull foo(0);
}

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

其他提示

当您在必须相互通信的应用程序之间有一个接口时,您就会真正体会到契约式设计。

如果没有合同,这种情况很快就会变成一场互相指责的网球比赛。团队不断地来回指责,浪费了大量的时间。

有了合同,责任就明确了。

调用者是否满足先决条件?如果不是,客户团队需要修复它。

给定一个有效的请求,接收者是否满足后置条件?如果不是,服务器团队需要修复它。

双方是否都遵守了合同,但结果却不尽如人意?合同不充分,问题需要升级。

为此,您不需要以断言的形式实施合同,您只需确保它们被记录下来并得到各方的同意。

如果你研究一下STL、boost、MFC、ATL和许多开源项目,你会发现有很多ASSERTION语句,这使得项目更安全地走得更远。

按合同设计!它在实际产品中确实有效。

弗兰克·克鲁格 写道:

盖乌斯:运行时会自动抛出空指针异常,在函数序言中测试这些内容没有任何好处。

对此我有两个回应:

  1. Null 只是一个例子。对于 square(x),我想测试结果的平方根(大约)是参数的值。对于设置者,我想测试该值是否实际发生了变化。对于原子操作,我想检查所有组件操作是否成功或全部失败(实际上是一次测试成功,n 次测试失败)。对于弱类型语言中的工厂方法,我想检查是否返回了正确类型的对象。这样的例子不胜枚举。基本上,任何可以在一行代码中测试的东西都是序言注释中代码契约的非常好的候选者。

  2. 我不同意您不应该测试事物,因为它们会生成运行时异常。如果有的话,你 应该 测试可能生成运行时异常的事物。我喜欢运行时异常,因为它们使系统 快速失败, ,这有助于调试。但是 null 示例中是某些可能输入的结果值。有一个关于永远不会回来的争论 null, ,但如果你打算这么做,你应该测试一下。

在 SOA 领域中做任何事情时不按契约进行设计绝对是愚蠢的,并且如果您正在从事任何类型的模块化工作,其中的一些部分可能会在以后被替换,特别是在涉及任何黑盒的情况下,这总是有帮助的。

我绝对会在军事级项目上使用合同设计来代替更具表现力的类型系统。

对于弱类型语言或具有动态作用域的语言(PHP、JavaScript),函数式契约也非常方便。

对于其他一切,我会将其抛在一边,转而依赖 beta 测试人员和单元测试。

盖乌斯:运行时会自动抛出空指针异常,在函数序言中测试这些内容没有任何好处。如果您对文档更感兴趣,那么我会使用可与静态分析器等一起使用的注释(例如,确保代码不会破坏您的注释)。

更强大的类型系统与契约设计相结合似乎是一条出路。看一眼 规格# 举个例子:

Spec# 编程语言。规格#是面向对象语言C#的扩展。它将类型系统扩展到包含非NULL类型和检查异常。它提供方法合同的形式以及后条件以及对象不变的。

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

我曾尝试在系统自动测试框架中使用契约设计,我的经验是,这提供了单元测试不易获得的灵活性和可能性。例如,每次执行操作时,都可以运行更长的序列并验证响应时间是否在限制内。

从 InfoQ 的演示来看,契约设计似乎是组件集成阶段传统单元测试的一个有价值的补充。例如,可以首先创建模拟接口,然后在释放组件的新版本后使用组件。

我尚未找到一个工具包,涵盖了我的所有设计要求,以通过.NET/Microsoft平台中的合同测试进行设计。

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

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

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

对于那些对小型感兴趣的人来说,这是一个链接 Java 参数验证 框架。它是作为普通 Java 解决方案实现的。

我发现 Go 编程语言没有使契约设计成为可能的结构。恐慌/延迟/恢复并不完全是这样,因为延迟和恢复逻辑可以忽略恐慌,IOW 可以忽略损坏的合约。至少需要的是某种形式的不可恢复的恐慌,这确实是断言。或者,充其量是通过契约构造(前置条件和后置条件、实现和类不变量)对设计的直接语言支持。但考虑到掌舵 Go 的语言纯粹主义者的意志坚定,我对所做的一切几乎没有什么改变。

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

我的观点是,断言在很多方面都很有用,以至于必须围绕它跳舞可能会让人头疼。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top