Jeff Langr从敏捷Java的角度,写了一本非常有趣的Java书籍:利用测试驱动开发的技术来雕琢代码。本书的目的是教会初学者如何使用Java语言,以及他和我都知道的最佳的开发方法——测试驱动开发(Test-Driven Development,简称TDD)。TDD承诺可以带来巨大的潜在价值,Jeff已经证明了这一点。我非常荣幸为这本书作序,也非常荣幸向您推荐这本书。
敏捷Java不止对初学者很有用,对于有经验的程序员而言,这也是一本能够带给您新的内容,帮助提升Java语言水平的好书。我不打算说这是一本认证指南,或者和其它很多”Java大全”相类似的书。这不是本书的要点。敏捷Java的要点在于帮助您熟练掌握如何使用TDD。TDD对于您将来的学习和日常的工作,都会有很大的帮助。
本书以面向对象的概念和思想作为开始。如果您了解对象的概念,那将有助于阅读这本书。如果您不了解对象的概念,那就要在继续之前,去熟悉面向对象。接着,每前进一步,您都会使用测试驱动开发的技术。如果您没有用过TDD,可能在一开始的时候会觉得有些不适应。但是,如果您象我们一样,尝试了TDD,TDD就会成为您的开发工具箱中常用的工具。
如果您已经安装了Java和Junit,请继续。如果没有,请一定阅读“搭建环境”,在开始真正的例子之前,正确地配置您的环境。一旦能够正确编译和运行一个简单的Java程序,就意味着准备就绪了。
Jeff要求您敲入测试和例子代码,我也赞同Jeff的要求。TDD的原则是通过实践来学习,而不只是阅读。您需要形成自己的关于开发节奏的看法。此外,敲入编程书籍中的例子是最好的学习方法。
在“敏捷Java”一书中,Jeff帮助您构建两个应用。一个是学生信息系统,另一个是国际象棋。在阅读所有章节的过程中,Jeff向您介绍了Java的基础。或许更重要的是,您会接触到某些最重要的深入的知识,包括接口、多态、模拟对象、反射、多线程,以及泛型。
第十课是关于Java的数学特性,在这节课中,我发现了利用测试来学习语言和库的新特性的方法。很容易理解类似BigDecimal的内容,您可能会想:“我已经明白了。”也许,暂时您的确是明白了。但是,当把学到的知识变成测试的时候,两件事情发生了:第一,阅读会丢失一些信息,通过编写测试可以掌握这些遗漏的知识。第二,测试记录了我们的学习过程,以及在学习中的思考过程。因为我已经养成了保留测试用例的习惯,所以我可以参考它们,唤起我的记忆。我甚至常常把书籍的页码索引到某些测试,当我回过头来希望进一步深入的时候,这些测试提供了注解。
第十一课关于I/O,提供了一个我不太熟悉,但是非常好的例子。因为我在Java方面从事的工作比较少,而且我常用的语言中没有和嵌套类相对应的概念,但是Jeff提供了一个非常好的例子,帮助我很好的使用和测试嵌套类。
当我写这份前言的时候,我真的进入了这本书,因为Jeff把我带到了我从没有到过的地方。我喜欢那样。第十二课关于模拟对象,第一个例子是敏捷软件开发者经常会遇到的问题:针对一个定义良好,但是还没有实现的外部API,我们如何实现增量开发?Jeff向我们展示如何通过接口定义来实现增量开发——必要的时候,接口定义来自文档——然后构建一个表示您对API的理解的模拟对象。编写针对模拟对象的测试,通过这些测试我们可以确信:API和我们所期望的、最终得到的实际代码一样,可以正常工作。这又是一个优秀的方法。
Jeff是一个相当好的老师。Jeff要求我们思考和做练习。他知道如果您和我打算开始学习,那么就必须实践:我们必须做练习。书中每一章都有练习。他建议我们思考这些练习,并且完成和我们不熟悉的主题相关的练习。在键盘上敲入代码,让这些练习深入您的大脑,并且在完成这些有趣的例子的时候,参照Jeff的指导。您会非常满意所做的工作。
敏捷Java:利用测试驱动开发的技术来雕琢代码,至少可以带给您三个好处:学会可能此前并不了解的Java知识,即使您不是一个Java初学者。学会在多种情况下,如何应用测试驱动开发(某些情况下,您可能发现自己很难找到合适的方法)。最后,在掌握敏捷Java的技巧之后,您可以把这种颇有价值的技术放到自己的专业工具箱里。
我很喜欢这本书,而且发现它非常值得一读。希望您也会喜欢这本书!
Ron Jeffries
www.XProgramming.com
Pinckney, Michigan
November 2, 2004
致谢
非常感谢所有想象力丰富的程序员,以及正在学习中的程序员,给予了敏捷Java很多富有洞察力的意见。
感谢我的前任雇主、Object Mentor的Bob Martin,Bob是一个伟大的导师,同时也是该丛书的编辑。Bob早期的复审,使得敏捷Java一书更加精确。非常荣幸我的这本书能够成为Robert Martin系列的一部分。也非常感谢Ron Jeffries为拙作说了很多赞扬的话。
ThoughtWorks的Jeff Bay做了非常重要的工作,他给了我所想要的东西——残酷、但是真实的反馈。Jeff负责提供每节后面的练习。
. 非常感谢Steve Arneil,Dave Astels,Tim Camper,Dan D'Eramo,Michael Feathers,Paul Holser, Jerry Jackson,Ron Jeffries,Bob Koss,Torri Lopez,Andrew Masters,Chris Mathews,Jim Newkirk,Wendy和David Peterson,Michael Rachow,Jason Rohman,Tito Velez,和Jeroen Wenting。他们校对了这本书,提出了大量的修改意见。
特别感谢Paul Petralia以及他的Prentice Hall的全体员工,是他们使我尽可能顺利的完成此书。
再次感谢我的姐姐Christine Langr,她提供了本书封面的图片,并且帮助我解决了很多设计问题。
同时,感谢San Antonio的Jim Condit为我提供了Nerf play和住所。并且,感谢我的妻子Kathy Langr提供了例子(而且陪伴我度过整个艰苦的写作过程)。
引 言
我是一名软件工匠。我的开发生涯中,大部分时间都在致力于快速构建解决问题的方案。与此同时,我也竭尽全力确保我的代码经过了细心的雕琢。我一直为完美的代码而努力,但是我知道这个目标是无法达到的,特别是为了发布产品,公司持续给你压力的时候。面对自己每天写的代码,我会有一丝自豪,然而当我回过头看昨天的代码,我经常会感到困惑:“昨天我到底是怎么想的?”这种挑战使我不断改进自己的代码——永远热切的盼望着下次做得更好一点,比上次痛苦更少一些。
敏捷Java为您描绘出一条成功学习和掌握Java开发的捷径。敏捷Java基于我在教授编程和自学一门新的编程语言中逐渐总结出来的方法:测试驱动开发(TDD:test-driven development2),这是一种引入大量底层反馈的技术。这些反馈使您很快看到行动的结果。使用TDD,您将学会如何雕琢您的Java代码,从而得到稳定的面向对象设计和高可维护、高质量的系统。
我已经使用TDD的方法开发软件超过四年,然而时至今日我仍然惊讶于这种方法带给我的好处。TDD提高了我编写代码的质量,而且每周都教给我新的东西,使我变得更富有效率。我也曾经使用TDD的方法在我的公司和Object Mentor(该公司一直用这种方法教授课程)编写和教授编程语言。
在学习TDD之前,我花费了超过十五年的时间采用“传统”方法来学习、使用和教授开发语言,没有使用测试驱动的开发。学生编写并执行一个例子程序,通过程序的输出获得反馈。尽管这是一种有效的方法, 但是我过去的经验表明这种方法会导致学生对语言细节不能很好的理解。
与之相对照的是,TDD所带来的大量迅速的反馈会持续地强迫您编写正确的代码,并且很快指出有问题的代码。经典的“编码-执行-观察”的方法可以提供反馈,但是反馈的速度要慢得多。不幸的是,当前的教学将其作为一种占主导地位的方法。
已经有人尝试了革新的方法来教学。上个世纪九十年代, Adele Goldberg 开发了一种用来教授年轻学生的软件——LearningWorks。这种软件通过让用户动态执行小段代码来直接操作可视化对象,用户可以立刻看到执行的结果。最近有一个Java培训工具采用了类似的方法,该工具可以让学生执行小段代码,在活动对象上产生可视的结果。
这些方法的问题是局限在了学习环境中。一旦您完成了培训,您依然必须去了解在不使用这些受限工具的情况下,如何从头构建您自己的系统。通过使用TDD,您被教会了一种不受限制的技术,您可以继续将其应用在您的职业软件开发生涯中。
敏捷Java尽最大可能采用了面向对象的方法,学习Java的困难部分在于需要“循序渐进”。为了写出一组有实际意义的类,您必须掌握的Java语言的最小集合是什么呢?
大多数书籍用“Hello world”作为第一个Java例子程序,不过这个程序似乎并不适合作为最初的例子:class Hello { public static void main(String[] args) { System.out.println("hello world"); } }. 这个简短的程序至少包含了一打您必须要学习的概念。更糟的是,除了这一打概念,至少还有三个您最好马上掌握的非面向对象的概念。
在这本书中,您将学到正确的从头开始编写代码的方法,之后您将会回过头来完全理解“Hello world”3。使用TDD,您可以很快编写出出色的面向对象的代码。在学习之初您仍然会有很大的障碍需要去克服,但是TDD使您免于在一开始就必须去理解一些非面向对象的概念,例如静态方法和数组。您可以适时的掌握核心的Java概念,但是您最初的重点应该放在对象上面。
敏捷Java向您呈现出一种与以往截然不同的解决问题的方法。它允许您假定从来没有一种叫C的语言,C是Java语言的基础,存在了有大约三十年的时间。C语言给Java语言打上了自己的烙印,妨碍您构建出色的面向对象的系统。使用敏捷Java,您可以在理解Java语言中这些来自C的遗产之前,学到正确的构建面向对象系统的方法。
预期的读者
我为想把Java作为第一门语言的编程初学者设计了敏捷Java;这本书同样会对那些熟悉TDD但对Java不了解的程序员有所帮助。有经验的Java开发者会发现,敏捷Java向您展现了一种崭新的,更好的实现目标的方法。
这个版本的敏捷Java涵盖了Java 2标准版(J2SE)5.0。
Sun提供了许多类库和API(应用程序编程接口),这些类库或者API增强了核心Java语言的功能。例如:JMS(Java消息服务)提供了标准的基于消息解决方案的定义。EJBs(企业Java Beans)提供了一种针对大型企业应用、构建基于组件的软件的方法。JDBC(Java 数据库连接)提供了与关系型数据库交互的标准接口。许多这样的高级APIs集合在一起,被称为J2EE(Java 2 企业版)。很多APIs需要一整本书来阐述。现在已经出版了许多有关J2EE的书籍。
本书只针对一小部分扩充APIs进行了入门介绍。本书用敏捷Java向您展现大多数企业应用都会广泛使用的技术,比如日志,JDBC,以及Swing。某些内容(例如日志)会带给您大多数应用都需要掌握的知识。还有些内容(例如Swing和JDBC)会让您对该技术有基本的理解。所有的内容会提供给您起步所必须的知识,并且会告诉您到何处去寻找更多的信息。
假如您正在开发移动应用,您或许正在使用J2ME(Java 2 微型版)。J2ME是一个面向资源限制型环境的Java版本,例如手机。相对J2SE,J2ME有很多显著的限制。本书不针对J2ME作专门的讨论,但是,本书讨论的大多数Java核心技术和概念同样适用于J2ME环境。
在使用任何Java技术扩展之前,您必须理解J2SE中提供的核心语言和开发库。敏捷Java将帮助您掌握这些知识。
本书所不能提供的
敏捷Java不能对Java语言的每个方面都进行详尽的论述。它提供了一种敏捷学习Java语言的方法。不是授之以鱼,而是教您如何钓鱼,以及在什么地方发现鱼。敏捷Java将教会您绝大多数的Java语言的核心概念。的确,当您完成本书的十五节课程,您将能够编写出有质量的Java代码。但是,这一目标要求您了解本书所没有提及的一些语言功能和细微之处。
一个熟悉所有Java语言细节的方法是仔细阅读Java语言规格说明(JLS)。您可以从这里得到JLS的第二版:http://java.sun.com/docs/books/jls。第二版的JLS涵盖了J2SE5.0之前的版本,但不包括J2SE5.0。我写这本书的时候,JLS第三版正在编写之中,您可以从这里得到它的维护检查版本:http://java.sun.com/docs/books/jls/java_language-3_0-mr-spec.zip。
如果您要深入了解扩展Java类库,Java API文档和源代码是最好的材料。
敏捷Java不是一本认证指南,它不能帮助您通过认证考试。一本好的认证教材会教您如何应付考试。敏捷Java同样也会帮助您破译代码质量低劣的原因。 敏捷Java将教会您如何编写专业的Java代码。
敏捷Java不会试图去溺爱你。学习编程是一种很大的挑战。编程包括思考和解决问题——不是容易到连白痴和傀儡都能够胜任的。我努力避免在本书中侮辱您的智慧,也就是说,我努力使敏捷Java更有趣和易于阅读。它是您和我之间的对话,更是在您将来职业开发生涯中和我的对话,同时它也是您和您的计算机之间的对话。
TDD 不承诺
对TDD有经验的开发者也许注意到他们的方法和我在敏捷Java中的方法有风格上的不同。有很多种方法来实践TDD。没有一种技术是完美的,或者绝对正确的。按照对您最有利的方式去做,按照最有意义的方式去做,只要不违背敏捷Java中的基本原则。
读者一定会发现需要改进代码的地方 4;即使是初学者,也请不要犹豫,告诉我您在敏捷Java中看到的您不喜欢的代码。把您的建议发送给我,我会在下一版中修正。您能改进几乎所有的代码或者技术。
在您学完敏捷Java的第一课后,会发现有大量的测试,好像这些代码是一气呵成,可事实上并不这样。每个测试都是由逐个断言构建而成,采用了比这本书所能忍受的还要小的增量一点一点完成的。请您一定要记住,使用TDD编写代码的时候,最重要的是采用尽量小的步伐并得到经常的反馈。当我说小的时候,我的意思是非常小。如果您认为您正在采用小的步伐,请尝试采用更小的步伐。
如何使用这本书
敏捷Java的核心包括十五节课程,每一课大约30页。您将像幼儿蹒跚学步一样,开始您的Java、TDD、面向对象的旅程,结束的时候您将拥有扎实的基础以从事专业Java开发。
十五节的核心课程是循序渐进的。您从第一节开始,完成每一节之后再进行后面的学习。一旦您学完了所有核心课程,您将对如何编写健壮的Java代码有深刻的理解。
假如您没有完成十五节核心课程,就不能假设您知道如何编写优秀的Java代码!(即使您完成了这些课程,您依然不能算是一个专家)。每一节课程都基于前面的课程。假如您在完成所有课程之前就停止您的脚步,您将缺乏完整的理解,这也许会导致您写出拙劣的代码。
每一节课程的开始,都有本节所要讨论主题的一个概要。后面紧接着详细的论述。我用文字介绍每一个语言特性,用测试代码详细说明它。我用相应的代码实现来展现每一个语言特性。点缀其间的是关于TDD技术、面向对象、优秀开发实践的讨论。
此外,我提供了三节扩展课程,以覆盖更多的Java主题。其中的两节课程介绍Swing,这是一个专注于用户界面开发的Java工具。这两节课程给您提供充分的信息,足以帮助您开始用Java构建健壮的用户界面程序。但是我最大的愿望是给您一些怎样用TDD构建它们的思路。第三节扩展课程简要介绍了一些大多数Java程序员都想了解的Java主题。
为了最有效的学习,您应该输入并执行本书中的每一段测试和实现代码。您可以下载这些代码(请看下一段),但是我强烈建议您亲自输入每一段代码。正确运用TDD依赖于您清晰的理解测试和编码之间来回转换所包含的韵律。假如您只是下载代码然后去执行它,您将几乎不会学到太多。键盘的触觉似乎传递了大量的知识。
但是,我又算什么,能要求您按照指定的方式去学习?您可以选择从这个地址下载代码:http://www.langrsoft.com/agileJava/code。我按照课程章节组织好了这些代码,也提供了代码的执行结果,也就是课程结束的时候它的状态。这将易于您在任何一点拾起课程,特别是很多例子一直沿用到了后续的章节。
练习
敏捷Java十五节核心课程的每一节要求您一点一点构建一个大学学员信息系统。我选择这个普通的主题有助您增量开发和扩展现有代码。每一节课程的最后都有若干练习。不同于学员信息系统,这些练习由 Jeff Bay 提供,要求您一步一步构建一个象棋软件。
其中的一些练习非常有挑战性,但是我强烈建议您完成每一个练习。通过这些练习,您可以在没有我的帮助的情况下,实战性的学习如何用Java解决问题。所有的练习给您又一次深入理解每一节课程的机会。
本书中的约定
代码紧随文字之后,目的是使其成为交流的一部分。假如我说“以下代码”,那就意味您接下来会读到一段代码;而我说“以上代码”,那就意味着代码会出现在相邻的文字上方。
敏捷Java交替使用文字和代码来表达思想。例如,也许我会说需要取消工资支票,也可能会说您应该发送一个cancel消息给 PayrollCheck 对象。这种方法有助于您在理解需求和用代码实现需求二者之间建立必要的联系。
类名约定使用单个名词,例如Customer。“您使用Customer类创建多个Customer对象”。为了增加可读性,我有时用类名的复数形式代指这些Customer对象:“这个集合包含了所有从文件系统加载的Customers”。
新术语第一次出现用斜体。绝大多数的术语在术语表中(附录A)。
贯穿整个敏捷Java,您都将根据规格说明并把它们转换成Java代码。按照敏捷过程的传统,我会用非正式的语言来描述这些规格说明。这些规格说明是用户需求,也叫stories:一个story是对多次对话的约定。您经常为了获取进一步的需求细节,需要和story的讲述者(有时候称其为客户)进行对话。在敏捷Java中,如果一个story没有理解,请试着往下读,读相应的测试看其如何解释这个story。
我在整个敏捷Java中用一个“讲故事”图标来强调stories。该图标强调口头的非正式的stories.
学习开发艺术的最好途径是和一位有实际经验的人一起工作。您将会发现很多编程的秘密。一些秘密是您为了掌握开发所必须知道的基本概念。其他的秘密向您展现需要密切注意的易犯的错误。您必须赢得这些挑战,记住您所学的,努力成为一名成功的Java程序员。
我用桥梁图标来标记这样的关键点(桥梁跨越了易犯的错误)。很多关键点在任何语言中都是有效的,不只是Java。