示例规范

2004 年 3 月 18 日

我在 2002 年参加了 XP/Agile Universe 的一个研讨会,当时“示例规范”这个短语让我想到了一种描述测试在 XP 中作用的方式。

(如今,谈论测试驱动开发 (TDD) 与测试有任何关系,这非常不合时宜,但就像 Jon 一样,我认为拥有一个全面的自动化测试套件比“副作用”这个词所暗示的更有价值。就像如果有人给我一百万美元让我爬上山一样。我可能会说这次徒步旅行的主要目的是享受大自然,但对我的钱包来说,副作用绝非微不足道……)

示例规范不是我们大多数人从小就被教导的思考规范的方式。规范应该是通用的,涵盖所有情况。示例只突出了一些要点,你需要自己推断泛化。这意味着示例规范不能是你使用的唯一需求技术,但这并不意味着它不能发挥主导作用。

到目前为止,在严格规范方面,即那些可以明确判断通过或失败的规范,占主导地位的想法是使用前置条件和后置条件。这些技术在形式化方法中占主导地位,并且也为 契约式设计 提供了基础。这些技术有其用武之地,但它们并不理想。在某些情况下,前置条件和后置条件可能非常容易编写,但在其他情况下则非常棘手。我尝试在一些企业应用程序设置中使用它们,我发现,在许多情况下,编写前置条件和后置条件与编写解决方案一样困难。示例规范的一大优势是,示例通常更容易想出来,特别是对于我们为之编写软件的非技术人员。(Timothy Budd 指出,虽然你可以用前置条件和后置条件来展示很多堆栈行为,但编写能够展示 LIFO 属性的前置条件和后置条件非常棘手。)

TDD 测试的一个重要属性是它们涉及双重检查。事实上,这突出了 XP 社区的一个有趣的小谎言。他们非常强调一次且仅一次地说事情,但实际上他们总是说两次:一次在代码中,一次在测试中。Kent 指出,这种双重检查原则是一项至关重要的原则,它确实是人类一直使用的原则。双重检查的价值与对双重检查的每一侧使用不同的方法密切相关。将示例规范与生产代码结合起来意味着你用两种截然不同的方式表达了这两件事,这增加了它们发现错误的能力。

形式化规范社区一直难以验证设计是否满足规范,特别是在没有容易出错的人类的情况下做到这一点。对于示例规范来说,这很容易。示例规范在理论上价值较低,但在实践中价值更高,这又是另一个例子。

一位契约式设计爱好者指出,如果你用测试来编写规范,那么供应商可以通过简单地返回对特定测试输入的硬编码响应来满足规范。我对这个问题的轻率回答是,如果你担心这个问题,那么测试与契约式设计之间的区别是你最不担心的问题。但这里有一个严肃的观点。测试总是会不完整,因此它们总是需要其他机制的支持。由于我是一个扭曲的心灵,我实际上认为这是一个优势。由于很明显示例规范是不够的,因此很明显你需要做更多的事情来确保一切都被正确地传达。传统需求规范最危险的事情之一是,人们认为一旦他们写了它,他们就完成了沟通。

示例规范仅在双方都进行协作而不是争斗的工作关系中有效。示例在设计团队中触发抽象,同时将抽象扎根于现实。你需要更多的东西——比如定期对话、领域驱动设计 等技术,甚至契约式设计的剂量。虽然我从未有机会完全使用契约式设计(即使用 Eiffel),但我经常从契约的角度思考接口。示例规范是一个强大的工具,也许是我最常用的工具,但绝不是我唯一的工具。

(有关示例规范的更多思考,即使不是用这个名字,也可以看看 Brian Marick 的著作。在他的网站上,可能有一个超级页面总结了他对它的看法。当然,找到它不如在你尝试阅读所有内容时阅读所有内容更有价值。)

一些后来的评论

在我第一次写这篇文章几年后,Cedric Beust 发布了一篇博文,批评了敏捷方法,这引发了一场小规模的博客争论。Jeff LangrBob Martin 做出了反驳,我再次通过提要发布了这篇文章。Jeff Langr 后来添加了一个 很好的例子,使用测试作为 Java 的 Math.pow 函数的示例规范。

2011 年 11 月 17 日重新发布