解决方法
选择一门语言。熟练地使用它。接下来的几年里,这将成为你解决问题的主要语言,而且只要你还在实践,这都是你要磨炼的默认技能。做这个决定是一次挑战。将各种选项仔细权衡一下非常重要,因为这是你构建早期职业生涯的基础。
如果别人要你去解决一个问题,而这项任务指定要用某种编程语言,那就让解决问题的动力来指导自己的学习。如果你想获得一份工作,而这份工作要求你使用某种特定的编程语言,那就使用那种语言来构建一个玩具应用,最好是个开源项目,这样你所期望的雇主就很容易看到你写的代码示例。不论哪一种,去找一名你知道的、接触得到的而且是最有经验的程序员,以后需要帮助时就找他。解决一个问题是花费几分钟还是几天,就看有没有一个可以随时帮助你的人。但还要记住,不能依赖那个更有经验的朋友来解决你所有的问题。
在学习第一门语言的过程中,一种改善学习体验的基本方法就是找一个实际问题来解决。这可以使你的学习根植于现实世界,从而为你提供第一个较大的反馈回路(feedback loop)。基于书本或者文章中那些短小的、人为设计的例子来学习是有局限性的,你将失去学以致用带来的好处,毕竟,你在工作中要做的是解决问题。改善这一体验的基本方法是寻求反馈回路。特别地,创建较短的反馈回路能协助测量你的进步。有些语言相比其他语言拥有更好的反馈工具,但不论哪种语言,你都能采取一些措施来搭建一个学习沙箱(sandbox),然后在里面实验。
Ruby中有一个交互式的命令行工具:irb。Rails中有script/console。类似地,Erlang有erb。Firebug提供了很多好用的方法,可以在Firefox网页浏览器中研究运行时的JavaScript。许多语言都提供了与此对等的工具。
有时,这些工具还不够,你需要更大的沙箱。Dave喜欢在他的IDE中始终放置一个空的Java类,当需要研究一个不熟悉的API(Application Programming Interface,应用编程接口)或者一项新的语言特性时,就可以直接拿来用。
当学到足以编写实际代码的时候,测试驱动开发(test-driven development)技术能让你基于小步骤前进,并确保自己的假设被检验。测试驱动开发是如此普及,当发现一门语言没有测试框架时,你甚至会有手足无措的感觉。不要犹豫是否写一些简单的测试来检查你对语言的理解,要么就直接让自己熟悉测试框架。
从采取近乎愚蠢的小步骤开始;等到你学会更多的东西,步子可以相应地放大一些。例如,Ruby语言有一项特性,可以把一个代码块应用到一个列表(list)中的每个元素上,并将结果收集到新的列表中。你可以编写如下的代码来明确你对这项特性的理解:
学习测试不一定只为了学习语言;也可以把这一过程用于学习其他人的库如何工作。久而久之,这些开发商的测试(Ade在伦敦测试自动化会议[London Test Automation Conference]的一次闪电演讲中使用了这个名字),[1]可用来检验当一个库升级到新版本时会不会弄坏你的系统。如果弄环了,这些测试便可指明新库就是问题的源头,因为这些测试使用的功能恰恰是那个库提供的。在一个构造良好的系统中,可以用它们来验证一套库的不同实现是否拥有了你所需要的所有功能。
最后,你将不再编写用于学习的测试,而是编写用于检查实际代码的测试,这些测试不再检查你对语言结构和API是否理解。时间长了,你会发现除了简单的单元测试(unit test)外,还有很多其他技术可用于验证你的工作,以及你跟其他团队成员的交流。
下面的讨论是关于如何通过学习一门新语言来学习不同的思考方式,但Ralph Johnson(《Design Patterns》,中译本《设计模式:可复用面向对象软件的基础》[机械工业出版社]的合著者之一)的建议也适用于第一门语言的学习。
问:那假如某个人确实想学习怎样以不同的方式来思考,他该学习什么语言?Ruby、Python或者Smalltalk?
答:我倾向于Smalltalk。但我倾向于什么并不重要。你应该根据周围的人来选择一门语言。你知道周围有谁爱好这些语言中的某一种吗?你可以常与此人交谈吗?或者更好一点,你能跟此人一起做个项目吗?
就我所知,学习语言的最好方法就是和一位该语言的专家一起工作。你应该根据自己认识的人来挑选一门语言。一位专家就够了,但一定要有一位。
最佳的情形是:基于一个项目,使用那种语言的项目,你可以经常性地与专家一起工作,哪怕只是每周四晚上一次也行。另一种情形也一样好:你独立完成一个项目,但每隔两周你都在午饭时把代码样本带给专家看。
靠自己也可以学习一门语言,但除非与专家交流,否则你需要更长的时间来领悟语言的精神。
——Ralph Johnson谈语言学习[2]
Ralph的建议与“找人指导”模式紧密相关,而且直接阐明了指导者对你的学习所能产生的影响。因此,在选择第一门语言时,是否能从就近的语言专家那里得到反馈就成了一个首要的考量。还有一点:选择了一门语言,就意味着选择了一个虚拟的实践社区,这个社区里有成型的惯用思想、社交集会方式和沟通机制。你应该利用这种支持网络,这样你就不只是在学习一门语言,而且实际加入了你的第一个“同道中人”社区。开始时,你所拥有的一切就是这个社区所倾向的工作类型,社区的边界,还有社区中的偏见和信仰。当选择学习一门语言时,你应该参加本地的语言狂热者集会(或者访问一个他们经常去的网上论坛),看看自己是否愿意属于这个社区。
如果能加入一个肯共享代码的社区,带来好处之一就是可以超越对简单语言结构的学习,并开始用惯用的语言思想来表达自己。但这只是开始,每一种语言都有其精妙之处,仅通过阅读别人的代码很难领悟到。
比如,XSLT中有“Muenchian方法”,Perl中有“Schwartzian转换”,而C中有“Duff设备”。这些技术都可以通过阅读代码学到。但是要领悟到它们为何重要,以及何时使用它们,则需要社区分享的经验。有时这种共享池仅存在于口头的传统中,你必须单独去跟一个人交谈才能获得这种知识。有时这种知识仅存在于邮件列表的归档中,或者一本在线的入门手册中,若没有上下文,很难理解它的重要性。在这类情形中,学习语言的新手不得不多年沉浸在社区中,才能把知识的管子接入到这个共享池。但现在这些语言的精妙之处常常被总结在一些书中,像《Effective Perl Programming》(Addison-Wesley出版)、《Effective Java》(Addison-Wesley出版)(中译本由机械工业出版社出版)、《Effective C++》(Addison-Wesley出版)。在掌握基本语法之后马上阅读这类书籍能大大加速你的学习过程,并帮你避免常见错误。
所有这些都能帮你深入挖掘第一门语言。几年之内,第一门语言将是你学习其他语言的框架。第一门语言学得越好,下一门语言学起来就越容易。尽管你将主要使用这门语言来解决日常问题并交付软件功能,你还是应该经常花一点时间来延伸自己在正常工作中对它的使用。在非常规的方向上延伸一下语言的使用能帮你发现语言的强项和弱点。
Eric Merritt的网志"The Shape of Your Mind"(你思想的形态)深入讨论了编程语言会对你解决问题的能力产生多么深远的影响。
从很多方面来说,编程语言都像一种塑形工具,像那些给帕拉卡斯[3]婴儿塑头盖骨、给汉族女人缠足或者给克伦邦[4]长颈族女人塑颈的东西。在编程语言的情形中,被塑的不是头盖骨,而是我们思考问题、形成想法并把这些想法运用于特定问题的方式。比如说,如果你写只用早期版本的Fortran(Fortran 77或更早)写过代码,很可能你不知道递归的存在。同样,如果你只用过Haskell,你对命令式(imperative)风格的循环也会知之甚少。
——Eric Merritt,"The Shape of Your Mind"[5]
深入挖掘入门语言也有危险,那就是深陷其中而止步不前。这门语言将在你整个职业生涯中成为你的“母语”而一直陪伴着你。不要让你对它的精通阻碍了自己对其他语言的学习和使用。健康的职业生涯会把你带入软件开发领域多姿多彩的语言洞天。每一种语言都为你提供了使用不同模式来解决问题的机会。在逐渐超越第一门语言的过程中,你应该寻找机会去学习一些采用迥然不同的方法来解决问题的语言。惬意于面向对象语言的学徒应该探究一下函数式(functional)编程语言。畅然于动态类型的学徒应该钻研一下静态类型。安逸于服务器端编程的学徒应该考查一下用户界面设计。一路走来,你肯定会对某些语言和解决问题的方法产生偏好,但要避免固执的、宗教式的亚文化把你推向一种“一刀切”(one-size-fits-all)的方法。这种语言技能的拓展是向着“师傅”般的博学多艺迈出的第一小步。
你不应该“嫁”给任何特定技术,而应该有足够宽的技术背影和经验基础,使自己能针对特定的情景选择好的解决方案。
——Dave Thomas和Andy Hunt,《The Pragmatic Programmer》(程序员修炼之道——从小工到专家),xviii页
[1]Ade Oshineye,"Testing Heresies"(测试邪说),参见:http://www.youtube.com/watch?v=47nuBTRB51c#t=23m34s。
[2]http://groups.yahoo.com/group/domaindrivendesign/message/2145.
[3]Paracas,南美洲秘鲁地名,隶属秘鲁西南伊卡地区皮斯科省。
[4]Karen,缅甸邦名,位于缅甸东南部。
[5]http://erlangish.blogspot.com/2007/05/shape-of-your-mind.html.