InfoQ

新闻

Spolsky论战Bob大叔

作者 Niclas Nilsson 译者 张晓庆 发布于 2009年2月23日 上午12时17分

社区
Architecture,
Agile
主题
编程,
软件测试,
设计,
敏捷技术
标签
行为驱动开发,
面向对象编程,
测试驱动开发,
设计准则,
质量

过去几周中, Joel SpolskyRobert C Martin (又称Bob大叔)之间有一场论战。它起源于 Jeff Atwood 和Joel Spolsky在 Stack Overflow广播的第38期播客 ,Joel提到别人经常给他这样的建议,说他应该把单元测试加到 Joel测试:改进代码的12个步骤 之中,成为第13步。而Joel不同意这样,他解释说:

对测试驱动开发有一些争论……应该给所有东西都写单元测试吗,诸如此类的东西……许多人读了Joel测试之后向我写信,说:“你应该有第13步:单元测试,所有代码要有100%的单元测试。”

对我来说这有点太空洞,好像你根本不需要的一些东西。敏捷编程总的观点就是不需要就别做,需要时再引入。我觉得很多时候对所有东西写自动化测试并不能对你有所帮助。

Joel基本上有两个异议,第一个是你把时间花在什么上面了:

但我觉得即使团队的单元测试覆盖率达到100%,依然会有一些问题。他们可能花费了相当多的时间编写单元测试,然而质量未必能得到相应的提高。我是说,质量可能提高一些,他们也有能力修改一些代码并有把握不破坏任何东西,但仅此而已。

Joel的第二个异议是测试套件非常脆弱:

但是我发现单元测试真正的问题在于,随着代码改进,你想修改的内容可能会破坏一定比例的单元测试。有时你修改了代码,不知何故,有10%的单元测试 失败了。因为你修改了某些设计...你移动了一个菜单,现在所有依赖菜单的东西仍然存在...而菜单移到了别处。所以这些测试都失败了。你必须能够重写这 些测试,以反应代码新的实现。

讨论继而转向了Bob大叔搜集的面向对象设计的SOLID原则,Bob大叔和Scott HanselmanHanselminutes最近的一期节目中对此做了回顾。Joel再次说道:

这是面向对象的设计,而他们把它叫做敏捷设计,实际上它真的,真的不是。这只不过是一些怎样设计类,类应该怎样工作的原则。坦白来讲,当我听到这些原则时,感觉就像官僚之极的设计,一定是没写过多少代码的家伙想出来的。

但是Jeff和Joel显然没有了解清楚Bob大叔和他与敏捷的关系。Bob大叔是达成敏捷宣言那次大会的发起者,几乎从Jeff和Joel出生时他就靠写代码谋生了。他还写了相当多公开的代码,所以Bob针对博客上的内容发表了他的想法。

我不认为TDD很神圣。我认为它是一个值得遵守的规则。我不会事先编写所有的测试。有一小部分测试在编码后再写只会更方便。甚至有一些代码我根本不会写测试,因为不值得写。但是这些都是例外情况。大多数代码我会先写测试。(不,Joel,这没有浪费我的时间。)

Bob也决定在敏捷上盖过Joel:

Joel说SOLID原则不是“敏捷”。(唉)。任何一个人和他的大叔都认为自己知道单词“敏捷”是什么意思。但是是我召集了那次会议,挑选了单词 “敏捷”。自从词组敏捷开发被创建以来,我一直在写关于敏捷开发的内容。我认为我知道什么是敏捷,什么不是。我也认为我有权力在这方面盖过Joel。 Joel,SOLID原则是敏捷的。

对Joel经历的修改代码会破坏10%的测试的情况,Bob大叔写道:

Joel继续抱怨测试脆弱的问题,说一旦修改了代码,一些测试自然失败了。Joel还引用了统计数据10%。这太傻x了,说明Joel对TDD了解 浮浅,根本没有学到任何东西(典型失败的书呆子)。如果你设计的测试有10%因为单点变化而失败,那你应该换个工作了。测试设计,与软件设计的任何其它部分一样,也需要解耦。

随着辩论继续,Atwood写了一篇博客,他称之为The Ferengi Programmer,他说即使他赞成SOLID原则从表面上看没有异议,但是确实有这样一种危险,就是过于依赖规则。

规则、准则和原则是从实践中提炼出来的宝藏,值得研究和尊重。但是它们决不能替代对你工作的认真思考。

Jeff所写的无可置疑。不仔细思考事情就不会有好的软件,大家写书、规则或者方法通常是意识到这样一种风险,大家奴隶般遵守你的建议,而没有意识到环境的不同,但是避免这些风险并不容易。总结起来就是http://www.infoq.com/cn/articles/better-best-practices,以及读者阅读建议时达到的技能级别。听从Jeff的建议意味着如果以德莱弗斯的衡量标准,你是想在那项技术上从高级初学者或者精通级别转到胜任。这种变化可以认为是你必须有意去做一些事情才能达到,并非随着时间流失就能够自然达到。

在更多公开的争论之后,大家决定Bob大叔应该去Stack Overflow播客广播,该期内容已于最近广播。 Bob大叔得以有机会描述SOLID原则背后的想法,Joel问了一些关于它们什么时候有用的问题。当Joel问道为什么该原则重要的时候,事情有了小小 的转机,Bob举例说能够分开部署组件,Joel继续问这个原则是否只在那些需要分开部署的大项目中才有用时,Bob声称他在这一点上同意Joel。

当谈到质量和TDD时,他们的分歧更加清晰起来(即使好像大家都接受测试像文档一样有价值这个观点)。Bob解释了创建测试套件的重要性,这些测试套件不能像Jeol经历中的那样脆弱,不要一点修改就破坏10%的测试,Bob还介绍了怎样做到这一点,比如通过逻辑和呈现层的分离、在界面显示内容之下 正确地测试。Jeff提出Unix社区通常是这么做的,首先写一个命令行程序,然后在其上封装一个UI来驱动它。

播客的最后,Jeff、Joel和Bob再次谈到了遵守建议的风险,以及编程社区的技术水平,他们一致认为太多的开发者根本不关心自己在做什么。总之,他们都认为多动脑筋是很有必要的,但感觉他们对开发软件的方式尚未有一致意见。他们三人都承认对变化做出响应很重要,但是要想做到响应变化,需要对设 计和测试投入多少是他们分歧的核心,也是整个行业普遍存在的分歧。变化是永恒的,我们如何处理变化却差别甚大。

这次讨论自然而然地在博客圈内引起了大量的讨论,论坛上的信噪比也下降很多。Dhananjay Neneto发表了与学习相关的一个想法深刻的评论,用以回应Jeff的这个帖子──The Ferengi Programmer,Dhananjay说道:

我认为这个帖子带来的大麻烦是,它的建议对初级程序员带来的害处要比好处更多。它可能鼓励他们做出取舍──甚至在学会做取舍的代价和含义之前。它也可能让他们远离此旅──仔细而明智的应用(需要在早期投入相当多的精力),并能帮助他们内化吸收那些原则。

这是一场可能永远持续下去的争论,也是一场杂音太多没什么意思的争论,但是仍然有一些有趣的东西,这些东西不仅仅是爱好的问题。它还引出了这样几个问题:

  • 有些东西经验丰富的人已经了然于心,我们怎样让新丁学习,并且学习时不要过于艰难呢?

  • 我们怎样应对变化,从哪一点开始为应对变化而预做准备的投入会大过产出,界限在那里?

  • SOLID原则只适用于复杂项目吗?什么时候该用,什么时候不该用呢?用和过度使用之间的分界线是什么呢?

查看英文原文Spolsky vs Uncle Bob

有一句话是值得思考的 发表人 Jeffrey Zhao 发表于 2009年2月23日 上午2时22分
改良的 TDD 与 UDD 发表人 Charlie Zhang 发表于 2009年2月24日 上午2时16分
漏掉了Kent Beck同学的反应 发表人 图灵 刘江 发表于 2009年2月24日 下午1时21分
不必严格意义上的TDD 发表人 zhang yongji 发表于 2009年3月10日 上午3时19分
  1. 返回顶部

    有一句话是值得思考的

    2009年2月23日 上午2时22分 发表人 Jeffrey Zhao

    有些东西经验丰富的人已经了然于心,我们怎样让新丁学习,并且学习时不要过于艰难呢?

  2. 返回顶部

    改良的 TDD 与 UDD

    2009年2月24日 上午2时16分 发表人 Charlie Zhang

    Bob 大叔的回答也是不错的:


    我不认为TDD很神圣。我认为它是一个值得遵守的规则。我不会事先编写所有的测试。有一小部分测试在编码后再写只会更方便。甚至有一些代码我根本不会写测试,因为不值得写。但是这些都是例外情况。大多数代码我会先写测试。(不,Joel,这没有浪费我的时间。)


    前半部分,我很赞同,TDD 不应该成为神圣的教条。Bob 提出的做法其实是一种改良过的 TDD,放宽了原先 TDD 对开发步骤顺序的严格限制。可见,我们(至少在 Bob 所说的例外情况下)并不需要针对所有实现代码,都必须先写测试代码。一个问题是,如果你不是每次都先写测试代码,后写实现代码,怎么叫 test driven 呢?

    Uncle Bob 说,大多数情况下,他会先写测试。具体他没说,是那种类型的测试,估计是单元测试。和 Uncle Bob 不同的是,一般我肯定不会首先去写单元测试,除非有绝对的必要,比方这些单元都很重要,而且它们的外部接口、使用方式和依赖关系已经相对稳定了,需要对外发布这些单元等等。

    尤其在开发阶段的早中期,在需求和架构都还不稳定的时候,我更不会首先把精力放在写单元测试上。我会尽量根据功能需求(use case)的基本流、扩展流以及架构的重要性等因素来设计测试,即便要做更小粒度的单元测试,也会首先考虑对一组、一群对象联合进行粒度更大一些的测试。我觉得这种方式效率可能会更高。

    UDD 认为需求、架构是软件开发的主要矛盾,每个单元相对于整体和全局来说是次要矛盾,当然这是一对辩证关系。所以,由于存在软件开发哲学观点的差异,TDD 更像是 bottom up 的开发,先保证每个单元积木块的正确,再保证整体、全局的正确;UDD 则更像是 top down(architecure-driven)的开发,先保证整体、全局的稳定和正确,再来完善局部细节。在实践中,其实这两者都是可行的。架构重要?还是单元重要?答案是在运动、变化的。

    由于任何带有边界的“系统”(包括类、单元在内)都存在 use case,所以 UDD 的单元测试,也借鉴了 use case/scenario 的设计方法。

    针对我所了解的国内的情况,UDD 认为:TDD 对开发步骤顺序的刚性规定(“必须”先写单元测试代码,后写实现代码)在实践中是没有必要的,而且很多时候没有必要从单元测试开始写起。另外,大多数代码都应该努力配上自动系统功能测试,以减少人工测试的麻烦。系统测试的完备性比单元测试的完备性更重要。

    资深敏捷 OO 教练
    www.zhangxun.com

  3. 返回顶部

    漏掉了Kent Beck同学的反应

    2009年2月24日 下午1时21分 发表人 图灵 刘江

    当然,比较简单:Joel Spolsky is wrong about my work
    参见www.threeriversinstitute.org/blog/?p=29

  4. 返回顶部

    不必严格意义上的TDD

    2009年3月10日 上午3时19分 发表人 zhang yongji

    否则就成为了教条。
    两种思维方式应该结合起来。

深度内容

模块化Java:声明式模块化

本文是模块化Java系列文章的第4篇,介绍的是声明式模块化。文中描述了组件如何以声明的方式来定义并组织在一起,而无需让代码依赖于OSGI API。

Ian Robinson和Jim Webber谈论基于Web的整合

本采访是在伦敦举行的QCon2009上记录的,Ian Robinson和Jim Webber探讨了如何将Web作为整合平台以及REST在理论上和实践中的好处。

项目管理修炼之道(精选版)

项目管理对于项目成败至关重要,但实践中每个项目都有自己的独特性,没有现成的解决方案可以套用。书中从应对实际风险的角度出发,讲述了从项目启动、项目规划到项目结束的整个管理流程,展示了作者的思考过程。本迷你书从原书中精选出5个章节。

那是鸟,还是飞机?不,那是超人!

在这个演讲中,Fred将会揭示敏捷的一些外在因素,并会重点关注敏捷获得成功的内在原因。从案例研究和真实的项目经验来看,Fred认为:工具、管理体系都不能让你变得敏捷。敏捷的成功,植根于士气高涨、充分授权的工作者身上,他们能够以不同以往的方式思考问题。

访谈和书摘:Eben Hewitt的新书《Java SOA Cookbook》

Java SOA Cookbook

Eben Hewitt的新书《Java SOA Cookbook》从Java实现的角度讨论了面向服务架构。Eben在书中讨论了SOA基础、工具、最佳实践和SOA治理等主题。

Mark Richard的《Java消息服务》第二版

Mark Richards的新书《Java消息服务》第二版覆盖了JMS的许多主题, 包括发布和订阅模式以及点对点模式,消息过滤和事务等。InfoQ与Mark谈论了跟他的新作。

模块化Java:动态模块化

本文是“模块化Java”系列文章的第三篇,讨论动态模块化,内容涉及如何解析bundle类、bundle如何变化、以及bundle之间如何通信。

让测试也敏捷起来

对于测试组织来说,敏捷方法带来的快速迭代却让测试本身变得困难起来:缺乏“足够详细的文档”,缺乏“仔细设计用例的时间”等等。在本演讲中,段念将与大家探讨如何在敏捷过程中进行测试。