您是否专业地使用按合同设计?这是您从项目一开始就必须做的事情,还是您可以换档并开始将其纳入您的软件开发生命周期?您发现设计方法的优点/缺点是什么?
我在研究生院课程中遇到了合同设计方法。在学术环境中,这似乎是一种非常有用的技术。但是我目前没有专业地使用按合同设计,而且我不知道有任何其他开发人员在使用它。很高兴从 SO 人群中听到它的实际用法。
您是否专业地使用按合同设计?这是您从项目一开始就必须做的事情,还是您可以换档并开始将其纳入您的软件开发生命周期?您发现设计方法的优点/缺点是什么?
我在研究生院课程中遇到了合同设计方法。在学术环境中,这似乎是一种非常有用的技术。但是我目前没有专业地使用按合同设计,而且我不知道有任何其他开发人员在使用它。很高兴从 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 语句,这使项目更加安全。
按合同设计!它确实适用于实际产品。
弗兰克克鲁格写道:
Gaius:运行时会自动为您抛出空指针异常,在函数序言中测试这些东西没有任何好处。
对此我有两个回应:
Null 只是一个例子。对于 square(x),我想测试结果的平方根是(大约)参数的值。对于二传手,我想测试该值是否实际发生了变化。对于原子操作,我想检查所有组件操作是成功还是全部失败(实际上是一次测试成功,n 次测试失败)。对于弱类型语言中的工厂方法,我想检查是否返回了正确类型的对象。名单还在继续。基本上,任何可以在一行代码中测试的东西都是序言注释中代码契约的一个很好的候选者。
我不同意您不应该测试事物,因为它们会生成运行时异常。如果有的话,您应该测试可能产生运行时异常的东西。我喜欢运行时异常,因为它们使系统快速失败,这有助于调试。但是null
示例中的 是某些可能输入的结果值。有一个论点是永远不会返回null
,但如果你要这样做,你应该测试它。
在 SOA 领域中做任何事情时,不按合同进行设计绝对是愚蠢的,如果您正在从事任何类型的模块化工作,这总是很有帮助的.
代替更具表现力的类型系统,我绝对会在军事级项目上使用合同设计。
对于弱类型语言或具有动态范围的语言(PHP、JavaScript),函数式契约也非常方便。
对于其他所有事情,我将把它抛在一边,依赖 beta 测试人员和单元测试。
Gaius:运行时会自动为您抛出空指针异常,在函数序言中测试这些东西没有任何好处。如果您对文档更感兴趣,那么我将使用可与静态分析器等一起使用的注释(例如,以确保代码不会破坏您的注释)。
一个更强大的类型系统加上契约式设计似乎是要走的路。以Spec#为例:
Spec# 编程语言。Spec# 是面向对象语言 C# 的扩展。它扩展了类型系统以包括非空类型和检查异常。它以前置条件和后置条件以及对象不变量的形式提供方法契约。
根据我的经验,单元测试和合同设计都是有价值的测试方法。
我曾尝试在系统自动测试框架中使用契约式设计,我的经验是它提供了单元测试不容易获得的灵活性和可能性。例如,它可以运行更长的序列并验证每次执行操作时响应时间是否在限制范围内。
从 InfoQ 上的演示来看,按合同设计似乎是组件集成阶段常规单元测试的一个有价值的补充。例如,可以先创建一个模拟接口,然后在组件的新版本发布后或发布时使用该组件。
我还没有找到一个工具包,涵盖了我在 .Net/Microsoft 平台中通过合同测试进行设计的所有设计要求。
我发现 Go 编程语言没有使契约式设计成为可能的结构。恐慌/延迟/恢复并不完全是因为延迟和恢复逻辑可以忽略恐慌,IOW 可以忽略损坏的合同。至少需要某种形式的不可恢复的恐慌,这确实是断言。或者,充其量,通过契约构造(前置条件和后置条件、实现和类不变量)对设计的直接语言支持。但是考虑到 Go 船掌舵的语言纯粹主义者的头脑清醒,我对这些所做的任何改变都很少。
可以通过在恐慌函数中检查最后一个延迟函数中的特殊断言错误并在恢复期间调用 runtime.Breakpoint() 来转储堆栈来实现类似断言的行为。要像断言一样,这种行为需要有条件。当然,当在断言之后添加新的延迟函数时,这种方法就会崩溃。这将在错误的时间发生在大型项目中,从而导致遗漏错误。
我的观点是,断言在很多方面都很有用,以至于不得不绕着它跳舞可能会让人头疼。
我实际上并没有每天使用按合同设计。但是,我知道它已被合并到D语言中,作为语言的一部分。
是的,它确实!实际上几年前,我为参数验证设计了一个小框架。我正在做一个 SOA 项目,其中不同的后端系统进行各种验证和检查。但是为了增加响应时间(在输入无效的情况下,并减少负载那些后端系统),我们开始验证所提供服务的输入参数。不仅适用于 Not Null,而且适用于 String 模式。或来自集合内的值。以及参数之间存在依赖关系的情况。
现在我意识到我们当时通过合同框架实现了一个小型设计:)
这是对小型Java 参数验证框架感兴趣的人的链接。它被实现为普通的 Java 解决方案。