TDD,测试代码可以代替文档吗?

曾经,我认为只要做好详细设计工作,软件编码就成为一种体力活。在我印象中传统软件工程理论好像是这么说得:分析和设计是软件生产过程中最重要的两个阶段,好的设计产生好的结果,坏的设计产生坏的结果,详细设计文档是软件过程中最重要的部分,甚至比代码还重要。国内某人的书中还提到,“只要有了详细设计,哪怕原来的开发人员都离开了,换一批人照着详细设计仍然能把软件做完”。一提到详细设计我的脑子里也已经出现了这样的影子:长长的(或者厚厚的)文档,详细到每个函数,甚至是每个函数参数的名字都定义好了,用这样的详细设计指导代码编写应该是一件多么惬意的事情啊。我推崇这种事无巨细的详细设计,认为只要是设计好就能够适应变化,并把软件项目的失败归咎与设计人员的知识、能力或经验不足。这种想法持续了很长时间,直到我有了实际软件项目的经验并开始单独做设计为止。 促使我思想转变的原因就是两个字:变化。我原来也知道变化对设计的影响,但是还是低估了变化对设计带来的冲击。现实中的详细设计只是一个看上去很美的东西,开始编写代码一个月后的详细设计就基本上不能指导代码编写了,甚至变成和实际代码完全两回事的东西,成了一堆废纸,原因还是那两个字:变化。是的,变化,在某个时间已经很缜密的设计,在下个时间就会变得漏洞百出,因为计划赶不上变化,通常情况下,详细设计文档从其完成的那一刻起就开始散发出“腐败”的味道。只有没有任何软件开发经验的人才会天真地认为一次做好完备的详细设计,然后就可以在其指导下完成软件开发,最终得到产品。 在传统的软件过程中,面对逐渐散发出“腐败”味道的设计文档,通常有两种对策,一种是安排专职的文档开发人员,每次在代码中修改设计都及时更新到设计文档中,以保持文档的“新鲜”。其实这种方法也只是一种看上去很美的东西,且不说多数项目组都不会有多余的人手专职做文档开发(有哪个项目组敢在项目还在进行中就说自己的人手足够了?),就算有这样一个文档开发人员,那么是否每次设计上的小修改都会通知到他(她)呢?显然不会,他(她)必须Review每一个开发人员的代码,并与大家随时沟通,发现与原始设计不一致的修改,这样会累死人的。那么是否可以由开发人员自己负责维护与自己工作相关的那部分文档呢?试想一下,当轰轰烈烈地代码编写开始后,或者头顶着产品交付倒计时牌,开发人员是否还有“闲情逸致”时不时停下正在Coding的手去重新修改一下设计文档呢?另一种对策是任由设计文档慢慢“腐败”,把主要力量集中在代码上,毕竟最终交付产品依靠代码而不是设计文档。这种情况下原来花费大量时间完成设计文档纯粹是浪费时间,哪怕是对自己人也没有丝毫用处,如果我是这个项目组中补充进来的新人,看到这样的文档和那样的代码,可能会得精神分裂症。 所以在敏捷(Agile)的词汇表里已经没有详细设计这个词汇了,取而代之的是“够用设计”,就是所谓的Not less not more, just enough。那么在敏捷这种短迭代周期,快速反馈体系中,是否还有必要编写一份用自然语言或图表展示的详细设计文档呢?Jack W.Reecves在他的论文《什么是软件设计?》中提出了“源代码就是设计文档”的观点,Jack说得设计文档和我本文提到的设计文档还不是同一个概念,我把他的观点引用在这里主要是为了和另一个观点做对比。Jack认为,“任何工程活动的最终目标都是某些类型的文档。当设计工作完成时,设计文档就被转交给制造团队。该团队是一个和设计团队完全不同的群体,并且其技能也和设计团队完全不同”【1】,由此看来,如果源代码不是文档,那么软件开发人员就不能被称为是工程师了,他们就和建筑工地上的工人一样,只是一群Builder。是否可以用源代码代替任何用自然语言描述的文档呢?我看还不太可行,源代码相对于自然语言来说过于晦涩,只有开发人员能够读懂(即便是开发人员,也不是所有人都能理解所有代码),显然制约了源代码作为文档的用途。不是有人在研究用自然语言编程序吗,如果这样的类似自然语言的源代码作为文档,倒是很合适。 上文提到,有一个与之对比的观点,那就是用测试代码代替源代码作为设计文档,但是前提条件是:使用TDD作为开发模式。很显然,源代码产生以后做单元测试用的测试代码已经失去了作为设计文档的前提,那就是每次迭代的设计文档必须早于或不晚于本次迭代的源代码产生出来。因为作为单元测试的测试代码是为已经存在的源代码量身定做的,并且已经受到这些代码的思维定势影响,失去了“设计”的意义。做为测试先行的TDD本身就是一个分析、设计、实现的过程,而测试代码又是这个过程的产物,测试代码理所当然的就是设计文档了。加之TDD要求在编写任何代码之前要先完成测试用例,这就是说任何新代码(新加的模块)都有配套的测试代码,自然而然的,测试代码就成为了新代码的描述文档,其中包含了如何建立使用新模块以及期望达到什么样的效果,而这正式设计文档的主要功能。源代码不适合取代自然语言的文档,因为源代码过于功能细化,逻辑纷繁复杂,分支和跳转加剧了理解代码的困难,缺乏对整个模块的宏观表达能力。与源代码相比,测试代码显然“单纯”很多,测试代码侧重于使用源代码,通常可以用作源代码的一份sample,所以它对整个模块的宏观表达能力要强于源代码。另外,测试代码通常是线性结构,比较容易看懂,从可理解性上看,测试代码比源代码更接近自然语言,因此测试代码比源代码更适合作为软件的设计文档。 那么测试代码是否可以完全取代自然语言形式的设计文档呢?我看还不行,原因有三: 其一,测试代码虽然比源代码容易理解,但它仍然是代码,不是所有人都能理解的; 其二,测试代码的宏观表达能力还是不如自然语言或图表; 其三,很多人习惯看文字而不是看代码,彻底改变人的习惯很难。 所以在TDD开发过程中,比较好的形式是自然语言的文档和测试代码相结合,用自然语言的文档做一个够用的设计就行了,这个设计只要详细到模块关系这一级别就足够了,各个模块的详细设计就由测试代码充当。 不管是“源代码就是文档”的观点还是“测试代码就是文档”的观点,目的都只有一个,那就是消除那些浪费人力物力做出来的、没用的、总是散发着“腐败”味道的东西。 [1] Jack W.Reecves. 什么是软件设计. 2002.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


什么是设计模式一套被反复使用、多数人知晓的、经过分类编目的、代码 设计经验 的总结;使用设计模式是为了 可重用 代码、让代码 更容易 被他人理解、保证代码 可靠性;设计模式使代码编制  真正工程化;设计模式使软件工程的 基石脉络, 如同大厦的结构一样;并不直接用来完成代码的编写,而是 描述 在各种不同情况下,要怎么解决问题的一种方案;能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免引
单一职责原则定义(Single Responsibility Principle,SRP)一个对象应该只包含 单一的职责,并且该职责被完整地封装在一个类中。Every  Object should have  a single responsibility, and that responsibility should be entirely encapsulated by t
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强烈推荐。原文截图*************************************************************************************************************************原文文本************
适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,它是针对软件开发中经常遇到的一些设计问题,总结出来的一套通用的解决方案。
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
迭代器模式提供了一种方法,用于遍历集合对象中的元素,而又不暴露其内部的细节。
外观模式又叫门面模式,它提供了一个统一的(高层)接口,用来访问子系统中的一群接口,使得子系统更容易使用。
单例模式(Singleton Design Pattern)保证一个类只能有一个实例,并提供一个全局访问点。
组合模式可以将对象组合成树形结构来表示“整体-部分”的层次结构,使得客户可以用一致的方式处理个别对象和对象组合。
装饰者模式能够更灵活的,动态的给对象添加其它功能,而不需要修改任何现有的底层代码。
观察者模式(Observer Design Pattern)定义了对象之间的一对多依赖,当对象状态改变的时候,所有依赖者都会自动收到通知。
代理模式为对象提供一个代理,来控制对该对象的访问。代理模式在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能。
工厂模式(Factory Design Pattern)可细分为三种,分别是简单工厂,工厂方法和抽象工厂,它们都是为了更好的创建对象。
状态模式允许对象在内部状态改变时,改变它的行为,对象看起来好像改变了它的类。
命令模式将请求封装为对象,能够支持请求的排队执行、记录日志、撤销等功能。
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 基本介绍 **意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为
享元模式(Flyweight Pattern)(轻量级)(共享元素)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结