编码工具
编码本质上来说是一种以键盘输入操作为主的工作。因此,输入代码速度的快慢很大程度上影响了一名程序员的效率。我是通过以下手段来提高输入代码速度的。
键盘布局
很多程序员都不知道我们使用的键盘布局(就是指字母键,数字键和符号键的所处的位置)并非只有一种。绝大部分人使用的是标准键盘布局,也被称为 QWERT 键盘(以左手上方那排字母键命名的)。但是很遗憾,这种布局的设计初衷其实并不是为了提高打字速度的。
我大概从一年多前开始学习使用一种叫做“Dvorak”的键盘布局。使用这种布局输入同样一段文字时,手指在键盘上的移动距离要比标准布局减少至少 50%。在转到 Dvorak 布局之前,我使用 QWERT 键盘有 20 多年了,但是我实际只花了大概一个多月的时间来完成键盘布局的切换。当然,在这一个多月的时间里,我每晚都坚持练习打字一小时左右。
其实,我现在使用的也不是标准的 Dvorak 键盘,而是一种叫 Programmer Dvorak 的键盘布局。这种布局在标准 Dvorak 的基础上,根据程序员的需要对数字和符号键的位置及输入方式做出了调整,目的就是提高程序员的输入代码速度。举例来说,使用 Programmer Dvorak 布局输入数字时,需要按 Shift 键,而输入符号(如 (), [], {}, =)时则不需要按 Shift。写代码时输入这些符号的次数显然要远远超过数字,这种变化对速度的提升效果不可忽略啊。
根据网上找到的研究资料表明,Dvorak 布局对输入中文同样会提升速度。程序员每天毕竟还是有不少时间会花在其他事情上面(上网找资料,聊天,写邮件等等),这些需要打字的事情效率提高了,同样有帮助。
代码编辑方式
相信很多程序员都听说过“Vi”这种文本编辑方式吧。可以说“Vi”就是为了编码而设计的,比起使用记事本那样的编辑方式要高效很多。我在所有的开发环境中(如 Intelliji 和 Sublime)都会安装 Vim(Vi improved)的插件。Vim 可以快速定位,查找和修改代码,另外还有很多非常强大的编辑功能。要学习 Vim,除了网上查资料之外,还可以通过游戏( http://vim-adventures.com/ )和挑战( http://vimgolf.com/ )来练习。
当然,我并不反对使用 Emacs,只是自己还没有时间学习,无法给出评价和比较。不过,网上有关 Emacs 和 Vim 孰优孰劣的讨论,我都是无视的。
开发环境和快捷键
编码时,我会尽可能使用快捷键,尽量不用鼠标。编码时使用鼠标,可以说是程序员的效率杀手。因为使用鼠标时程序员的一只手就会离开键盘,导致输入代码的间隔加长。其实,使用 Vim 和快捷键的道理是一样的,就是为了让双手尽量少的离开代码输入区(字母键,数字键和符号键)。如此说来,使用键盘的“上下左右”键也会影响效率,因为这些键通常在键盘的右下角且离开字母键区比较远。
常用的开发环境一般对快捷键的支持都不错,除了预定义的快捷键之外,还可以自定义快捷键。另外,在 Eclipse 和 Intelliji 中有如 mousefeed 和 key promoter 这样的插件,他们会在程序员没有使用快捷键的时候给出提示,或者提醒程序员为一些使用到但没有对应快捷键的操作设置快捷键。
我鼓励程序员根据习惯来设置自己顺手的快捷键,不要拘泥于开发环境预定义的那些。遇到自己的快捷键和预定义的冲突时,如果预定义的操作并不使用或很少使用,可以果断解除原有设置,使用自定义快捷键。而要熟练掌握快捷键并没有什么窍门,坚持在编程练习和工作中多使用就可以了。去背诵那些快捷键手册是没有什么用处的。
我目前主要的开发环境是 Intelliji 社区版(针对 Java 和 Scala)和 Sublime(其他语言或者工具,如 Ruby, Python, PLSQL, Robotframework 等等)。他们都是免费的开发环境,可用的插件很多。
敏捷工程实践相关的工具
上面提到的编码工具对效率的提升都很直接。下面我将要提到的工具,和程序员如何来写代码和设计代码有关。
单元测试框架
测试驱动开发(TDD)是我推崇的编程和设计方法,可以帮助程序员写出简洁和设计合理的代码。而 TDD 中产生的单元测试,通常是用某个单元测试框架(UT 框架)来运行的。UT 框架这个工具并不是 TDD 所必须的,因为编写和运行测试本身并不复杂。不过使用了 UT 框架之后,可以简化单元测试编写,运行和组织,对于测试的维护和管理还是有帮助的。
我使用的 UT 框架包括 JUnit(Java),Scala-test(Scala),RSpec(Ruby)等等。有些 UT 框架提供了一些强大的功能,在使用这些功能时要小心,因为用得不好可能会影响单元测试的可读性。举例来说,很多 UT 框架都提供了数据驱动测试的功能(Data Driven Test)。虽然说这个功能可以简化单元测试的编写,但是我使用后发现,如果大量使用数据驱动测试,会使得单元测试的可读性下降。原因在于数据本身不一定能表达测试和设计的意图,从而导致测试难以维护。
重构工具
重构指的是在不改变代码行为的前提下改善代码的设计,它是测试驱动开发中的重要一环。以 Java 为例,Eclipse 和 Intelliji 都提供了很好的重构工具支持,可以大大减少重构的工作量。不过,在使用重构工具之前,程序员应该很清楚为什么要做某个重构(如发现了代码臭味),以及要使用哪种重构方法。有些稍微复杂一点的重构(如移动方法),因为开发环境对其支持有限,无法通过工具来实现时,就需要程序员手工来完成。实际上,我建议每个初学重构的程序员一开始不要使用工具重构,而是手工重构代码。这样对于学习如何小步重构,在重构中如何让测试失败的时间最小化,都是很有帮助的。
由于代码的复杂性,有时即使是看上去很安全的重构(如重命名),因为重构工具还不够智能(不同开发环境的表现也不同),还是可能出现修改之后的代码发生了行为上的变化。因此,即使使用工具来重构,也需要有测试来确保代码原有的行为没有发生变化。切不可因为使用了重构工具,就在不写测试的情况下面对代码进行修改。
Mock 框架
Mock 框架指的是在单元测试中使用的那些用来隔离被测代码依赖的工具。还是以 Java 为例,Mock 框架其实很多,如 EasyMock,JMock,Mockito 等等。和 UT 框架及重构工具类似,使用 Mock 框架可以简化在单元测试中隔离依赖的工作,避免手工写隔离代码的麻烦。同样和重构工具类似,我建议初学 Mock 的程序员先不要使用这类框架,而是手工来隔离被测代码的依赖并做相应的验证。我遇到过很多会使用 Mock 框架的程序员,不会手工写 Mock 的代码。究其原因还是他们并没有理解在测试中到底要如何来隔离依赖,以及要如何来做验证。
有些 Mock 框架(如 PowerMock)过于强大(比如可以隔离一些静态或 final 方法),我并不推荐使用。原因在于隔离依赖的目的是让被测代码的设计更加合理。如果在单元测试中要为被测代码隔离一些静态或 final 方法,那么用 PowerMock 固然很方便,但是这样做会让程序员忽略代码可测性差的问题。在这种情况下,只做到为了写测试而去隔离依赖是不够的。程序员应该考虑是否先调整代码的设计,使得测试更容易写,并且依赖更容易隔离。实际上,如果改善了代码的可测性,一般的 Mock 框架也就够用了。
自动运行单元测试的工具
我最早是不用这种工具的,因为通过手动运行单元测试(使用快捷键)体验到测试驱动开发中的测试失败和通过,是实践和练习 TDD 非常重要的一步。后来习惯 TDD 之后,我尝试了一个叫 infinitest 的工具(Eclipse 插件),可以在保存代码的时候自动运行受影响的单元测试。一开始感觉不错,但是我试用了一段时间之后,发现这个工具运行测试不太稳定,经常莫名其妙的出问题,而且有时还会运行很多不相关的测试。
其实,在 Eclipse 和 Intelliji 中可以定义一个重复运行上一次单元测试的快捷键。只要恰当的设置,也可以做到一键保存代码并运行测试的效果。而且,这样还可以选择需要运行测试的范围,避免运行那些无关的测试。所以,这类自动运行单元测试的工具,我现在不推荐使用。
小结
上面介绍了不少与写代码和设计代码相关的工具,相信大家已经发现了这类工具的一些共同之处。首先,使用这些工具前要明白相应实践的目的和原理。其次,即便工具可以提高效率,以手工的方式来实现代码仍然是一种很好的学习方法。最后,现在很多工具都存在过度开发的问题,通常是因为忽略了它们自身所服务领域实践或原则的本质目标。因此,在使用这些工具时,程序员要学会取舍,真正做到让工具“为我所用”,而不是“为了工具而工具”。
编程语言
最后,我想说“编程语言”对程序员来说也是一种“工具”。我觉得讨论编程语言的孰优孰劣没有任何意义。我一直很反对网上各种有关语言好坏的所谓论战,程序员为什么只能学一门语言呢?如果你不会一门编程语言,你就无法理解那种语言解决问题的思维模式。我觉得一个程序员至少要学一门面向对象语言,一门函数式语言,以及一门动态语言,不然他的人生就是不完整的。可惜的是,我看到过很多程序员都只会一门编程语言(其中 Java 居多,而 Java 则是我见过“语法和语言特性”最弱的一门主流语言了),更有甚者还会鄙视或者拒绝学习其他语言。对于这样程序员,我只想说“虽然你手上有一把榔头,但这不表示世界上所有的东西就都成钉子了”。
时至今日,很多语言都在相互学习和渗透。.Net、C++ 和 Java 陆续支持 Lambda 表达式(函数式编程)就是一个很好的例子。我非常喜欢函数式编程中的一些语言特性,如不可变量,高阶函数等等。这些特性都可以帮助程序员写出更加简洁和可读的代码来。另外,尝试一下多语言编程,是件非常有趣的事情。我最近就试过用 RSpec 来测试驱动开发 PLSQL 的代码。说到底,项目或产品开发时,使用的编程语言也应该是“浮现”出来的。哪种语言解决问题最有效就应该用哪个。
有些程序员说学语言要忌“多而不精”,这点我很赞同。不过,对于“精通一门编程语言”的定义,每个人的理解不尽相同。我自己的定义是(以 Java 为例),熟练使用所有可以简化代码的语法,以及熟悉基本类库的使用(比如数据类型和集合类型),其他一些类库可以视需要再学习。另一方面,我觉得没有必要强求“精通”了一门语言之后再去学下一门语言。毕竟对语言的精通程度是和你在练习和工作中使用这门语言的时间长短有关的,而且语言本身也是一个不断发展的东西。通常抱有这种想法的程序员,只是为了逃避学习新语言找借口罢了。
总结
程序员的工具远远不止我上面提到的这些。很多开源的技术框架和工具软件,我觉得都应该算进来。好的程序员其实都很“懒”,因为他们总是想着把复杂繁琐的事情变得简单快捷,可以花更少的时间达到同样的效果,所以他们选择了一些“工具”来提高效率。同时,好的程序员也很清楚使用这些工具背后的原因,只会根据需要来选择合适的“工具”,不会“为了工具而工具”。对我来说,如果使用工具可以帮助提高工作的效率,就会考虑使用或试用。反之,如果降低效率,则坚决不用。如果提高效率不明显,则要慎用并要持续关注效果。
要用好工具都离不开练习和工作中的不断使用,希望本文可以帮助程序员找到合适自己的工具,从现在开始,从“我”做起,为了提高效率而努力。
感谢侯伯薇对本文的审校。
给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。
评论