【原】书摘:大神和他的朋友们——《Coder at Work》审稿有感

  

 

  最近有帮图灵俱乐部的《Coders at Work》审稿,主要负责Ken Thompson和Joshua Bloch的这两个部分,两位大神现在都在Google,前者是Unix之父,并且发明了C语言的前身——B语言,他“对操作系统理论的发展的贡献,尤其是实现了UNIX操作系统”,使得他在1983年时和Dennis Ritchie一同获得了计算机科学的最高荣誉——图灵奖;后者Joshua Bloch现任google的首席Java架构师,他之前在SUN工作,工作主要围绕着Java语言特性和库展开,他写的《Effective Java》获得当年的Jolt大奖,他还写过《Java 并发编程实践》和《Java Puzzles》(这两本可是Java面试和考SCJP必备)两本技术类畅销书。《Coders at Work》整本书都是以访谈录的形式组织,因此表达方式上都比较口语化,娓娓道来、平易近人却又充满了真知灼见,用简洁的语言表达深刻的哲学思想,不像《编程之道》里讲的那么玄乎。在内容上,囊括了优秀的编程实践和设计思想,还有大神艰辛而又充满热情的成长道路以及几十年的技术生涯带来的人生感悟。

  Ken Thompson是属于那种传统意义上的Unix黑客,如今胡子一把了还对编程如此痴迷。从小学开始他就对逻辑运算和二进制就表现出了强烈的兴趣,大学就读于伯克利电气系(那个年代还没有计算机科学系),从那时开始接触计算机,从此一发不可收拾,热爱技术,酷爱钻研的他,很快就以牛逼的理论水平和实践能力在同学和老师中赢得了很高的评价,当过助研、当过讲师,毕业时Bell实验室的人三顾茅庐才把他请出山,最终的那一次上门经历比较搞笑,Ken回忆说,"他来到我的公寓,并且说希望我去他们那面试。我告诉他不去。他说:'哥们儿,就当是一次免费旅行吧。你去了干什么都行。’我说:“嗯,坦率点说,我对你们这个工作根本不感兴趣。不过有一次旅行,我倒很高兴。因为我在东海岸有很多朋友我想去见一下。”于是他就这样很“随意”的加入了Bell实验室,然后他就参与到Multics这个著名的分时操作系统项目中,然而由于种种原因,最后这个项目失败了,当整个团队都不愿再去啃分时OS这块硬骨头时,他仍凭借一腔热情坚持了下来,后来靠着老婆孩子回娘家去的一个月,没日没夜的干,终于和Ritchie一起把Unix给整出来了,他说,在那个月里“我每27~28个小时才睡一次,每次就睡6个小时”,“虽然如此,仍然精神饱满”,也听不见孩子哭闹了,老婆也不催着上床睡觉交公粮了,“他们在的时候,生活是必须遵循一天24个小时的”,现在他们走了,我想干嘛就干嘛!(看来要想在科研上干一番事业出来,光有热情还不够,老婆孩子回娘家也是必须的。。。)除此之外,他在开发Unix的过程中,还发明了B语言,这玩意儿后来被Ritchie发展为C语言,C语言和它的衍生品们“毒害”了一代又一代的青年的思想,几乎霸占了整个编程语言的江山。同样今天也很难在操作系统领域中找到不属于Unix和它后代们的异类。对整个计算机业界产生了如此深远的影响,所以,Thompson拿到图灵奖也的确是实至名归。

  Joshua Bloch相对于Thompson来说是个晚辈,他成长于70年代,那时的天下还属于DEC的。这小伙当时不如上面的那位前辈对技术的那么狂热,表现出来的更多是“觉得有意思”或者是“感兴趣”(这也就是为什么他的成就不如前者)。从Colombia和CMU毕业后,几经风雨,在96年时才加入SUN,在那里第一次接触到了面向对象的语言,后来他领导了并参与了很多给Java带来翻天覆地变化的机制,比如Java并行编程库java.util.concurrent以及java 5中的泛型(generics)等。这时他才开始有名,其实很多人认识他主要还是通过Java领域的两本经典图书《Effective Java》和《Java Puzzles》(这两本书也成为了天朝Java程序员考SCJP认证和面试Java职位必备)。

  在两位大神的访谈中,他们都对自己所认同的优秀的编程实践提出了看法。在对编程语言的观点上,Thompson是C语言的绝对忠实用户(废话,自己的孩子能不疼吗?),他自称自己“所有的无论是正式还是非正式的工作都是用C语言完成的"。他说总之他”拒绝使用C++“(C++在Google内可是属于绝对的头号开发语言)。众所周知,C++语言同样诞生于Bell实验室,Thompson的同事Bjarne Stroustrup创造了他,Thompson以前关于C++的过激言论曾经导致两人关系紧张,他说,即便如此,他还是觉得“C++是一门糟糕的语言,很多事情(语言特性)都是半途而废,最后互不相干的想法汇总成了一个垃圾堆”他说C++是如此的庞大以至于每个公司和个人只能用到其中很小的一部分子集(个人认为,显然这对代码的可复用性造成了很大的障碍)。他还说造成C++今天这个格局,与Stroustrup和C++标准委员会的没有原则是分不开的,他们“将每一种存在的功能都加入到这种语言中,那不是一种清晰的设计,只是把所有的东西拼凑在一起”(深有同感!),所以他认为C++语言已经遭受到了重创,复杂度难以驾驭。对于此,Joshua有精辟的见解,他说“当你改进一个成熟语言的时候,你必须仔细考虑能力与复杂度之间的平衡,实际上,复杂度跟语言的功能数量间至少是平方级关系,为一门老语言加入加上了一个新的功能,通常就意味着加入了一大堆的复杂度。当一种语言已经达到或接近程序员的理解能力的极限时,那么加入如何复杂性都会急剧增加理解的难度”(难以理解,更不用说复用了!)。Joshua的这个教训源自于Java 1.5中对泛型和通配符的引入(类似于C++的Template机制)在社区中所引起的广泛争议。我觉得按道理来说引入泛型的确可以减少很多bug,一个很明显的例子是集合中的元素不用再每次取出都要做向下转型了,这样可以再编译时尽早的发现问题;同时参数化类型和参数化方法的确可以给程序员带来很多实现上的灵活性。然而实际的情况却与想象中不同,Java中的泛型机制明显给程序员带来的理解上的难度大于它所带来的好处。这让我想起了C++,一堆在实际使用中浅尝辄止的特性最终成为了各大IT公司笔试面试中“高区分度”的偏题怪题,或者是少数热爱装B的程序员用来向大家炫耀用的奇巧淫技。Joshua说,从这件事儿得到一个教训,“在你能够确保大部分软件从业人员能高效的使用该新特性,而且这一新特性会让他们活得更好之前,你不应该加入这一新特性”。他还说,应该仔细聆听用户的声音,不能因为一个东西简洁,或者少部分人说好,就把它加进来,“因为你可以放进去的永远比应该放进去的多”。我个人认为,语言的设计应该是个民主的决策过程,因为每一个修改都会牵扯到大量的人,所以需要聆听大量的来自于社区的声音。Joshua说,他一直都信奉“简单即为美”,过度设计和过度复杂的东西最终还是会被证明是有害的。我觉得实际上我们大多数程序员使用Java,仅仅是因为它的简洁和高效,这也是当年其能战胜C++坐上编程语言第一的原因。很不幸的是,随着时间的推移,以及为了与C#的阵营的竞争,我们看到了Oracle毫不犹豫的把闭包(Closure)加入了最新的Java规范,我也不愿意看到诸多Java程序员最终成为商业竞争的牺牲品,更不愿看到Java变成第二个C++。Thompson说每个人都在骂C++,每个人都在埋怨他的不好,但Google还是使用C++作为主流语言,关键在于公司可以省去针对刚入职的学生教育和再教育的费用,然而Joshua认为这是受公司“DNA”的影响,因为以前Google大部分系统都是基于C++构建,所以现在还是继续使用C++,除此之外,他还说程序员在选择实现系统的编程语言上可以在开发效率和运行效率中做一个很好的权衡,如果一段程序非常关键,即使提升一丁点儿性能都会带来整个业务响应时延的一些下降或者吞吐率的一些上升,那么这段程序就应该使用C或者C++来编写。

  对于编程风格,Joshua提出了两个原则,一是程序要具有很好的可读性,那么首先需要有表意清楚的函数名、变量名,然而说起来容易做起来难,在编码大型系统的过程中这个问题曾经不止一次的困扰过我,Joshua提出的方案是,随身携带一本韦氏学院词典,我觉得这是一个不错的实践,除此之外,我认为也可以通过大量阅读优秀的开源项目代码来借鉴,还有个方法我经常用的就是在JDK API和MSDN中搜索与我所需要的函数、变量类似的名字,也算是一种借鉴吧。其次是要有表达简洁清晰的注释;二是尽量减少代码的冗余,也就是著名的“Don't repeat yourself”原则,如果你不得不在系统内做复制粘贴代码的操作,那么一定说明你的设计有问题,总之不能纵容自己的这个坏习惯。对于第二点,Ken提出了类似的看法,他也觉得存在冗余的代码是脆弱的代码,对于脆弱的代码,有时修改一个小小的地方甚至需要修改上十几处位置。他认为代码就应该写得健壮简洁,珠圆玉润,完美无缺,这么多年来他一直坚持着“简洁详实、短小精悍”的编码风格,这样“没有什么花哨活儿,任何人都能看懂”。

  对于调试方法,两位大神都表示几乎没用过调试器,一致表示自己调试时都是通过添加打印语句(比如printf)来跟踪想了解的代码。Joshua认为,再精美的调试器也不如程序员的大脑!关于这一点,Linus一直都表现出来相同的固执,他说调试器让程序员变得懒惰,使得他们不能从整体上来理解代码,他的这个观点阻碍了Linux内核调试技术的发展,所以至今大多数人进行内核调试还是使用的printk。这个观点我只能表示大部分赞同,在有些情况下,比如代码存在一个逻辑上的小bug,我还是渴望使用断点和单步能快速修复它(Windbg调试windows内核的日子真美好,哎,KGDB真NM难用)。在断言的使用上,二者有不同的看法,Joshua认为应该使用断言来确保到达断言时程序所应该处于的状态以及后续程序运行中的“不变量”,并且断言可以作为一种代码的辅助理解方法,当你时隔六个月或者当别人来阅读你的代码的时候,通过断言可以知道:哦,原来进入这个函数时需要确保程序处于这样一种状态。而Ken表示自己从不用断言。其实这个原因很好理解,毕竟前者做的是应用程序,后者做的是操作系统,Java应用中断言悲剧时,顶多抛出个Exception(并且含有丰富信息),而OS断言悲剧的话内核就panic了。不过如今在Linux内核中,断言机制也被引入了,它就是BUG_ON这个宏。

  谈及设计和编码实现,Joshua分享了自己在设计API时的一些经验。他说首先最重要的是了解你到底要设计什么,也就是需要解决什么问题,说简单点就是需求分析,但是这个需求分析必须是深层次的,比如当用户期望在系统中增加几个特性时,你必须从用户那弄清楚几个问题,“为什么?你想用这个系统做什么?你期望它怎么发展?”直到弄明白用户真正需要软件去做的事情,这些就是用例,它们就是用来衡量你的API设计到底是否优秀的基准。接下来就到设计实现阶段了,Joshua建议先编写用例,再编写API原型骨架,然后逐步完善API,也就是先写使用API的代码,然后再写实现它们的代码,这个设计思路类似于TDD的思想。他还说在这个过程中应该不断对API进行重构,不合适的就要丢弃,太大的就要拆分,“总之在API设计中要保持一个原则:疑则不用,构建可运行的最简系统,然后毫不留情的重构。”总的来说,Joshua认为API的设计有两点非常重要,一是要擅于倾听并且把握住用户真正的需求,如果你情商不够高,或者不懂共情(empathy,心理学词汇),就不应该掺和到API设计,编程语言设计和GUI设计中来,二是尽量保持简洁,疑则不用,时刻牢记避免过度设计,因为复杂的东西最终会被证明是有害的。(Joshua曾经在OOPSLA上做过一个关于该话题的著名演讲,演讲的题目是《How to Design a Good API and Why it Matters》,里面详细解释了他的API流程,感兴趣的童鞋可以去参考下。)我在看《Unix编程艺术》时,读到过这样一个观点:保持简洁是Unix世界中最根本的一条哲学思想,Java语言也延续了这一风格,这也是奥卡姆剃刀法则(Occam‘s Razor)在编程世界中的体现,关于“尽量保持简洁”,Tony Hoare的图灵奖获奖感言中有一句很犀利的话,“设计一个系统有两种方式:一种是尽量简单,这样明显不会有什么问题;另外一种是,尽量复杂,这样没什么问题会很明显。”第一种方法其实更难,“它需要从复杂的现象中发现简单规律的那种投入和犀利的洞察力,甚至是那种灵感,同时你还要能够接受当你的目标受限于客观条件时,或者目标之间存在冲突时,你可以作出妥协。(C++委员会从不妥协!)在这点上Ken也提出了相同的设计观点,他对比了MIT领导的MULTICS和他自产的Unix,他说MULTICS的失败归咎于“过度设计,过度建造,什么都过度,结果等同于无用。”结果MIT还声称这是一个巨大的成功,显然这纯粹是放屁。在代码实现上,他认为有两点经验,一是代码都是会腐烂的,所以要经常进行重构或者丢弃,不能有丝毫的怜悯之心;二是避免过早优化,一开始做的时候尽量保持简单,有时候简单的穷举就可以工作的很好,过早的优化只能增加复杂度,并且不一定带来好处,因为这一段代码也许只会在系统中偶尔被运行。他说,对简洁的代码进行优化,要比让优化的代码简洁容易的多。所以应该在实现之后站在系统全局之上再来考虑优化,优化只能针对那些在系统中关键的、经常运行的代码。

  最后,两位大神都谈了一下“优秀的程序员应该具备那些素质”,以及干了这么多年“挨踢”的感想。Ken觉得一个优秀的程序员最重要的品质是需要有热情,需要有钻研和动手两方面的热情,他说这也是他在面试新人时最注重的品质(这种面试我有点怵),同时他也认为程序员需要有艺术细胞,这样才能创造出美和简洁的东西。Joshua认为,优秀的程序员首先应该要觉得编程是件有意思的事,需要对编程充满兴趣,其次要保持一个开放的心态,能够超越各个社区、阵营中的狭隘观点,接受并吸收来自于不同世界的优秀思想,最后需要有一点美学追求,一些数学技巧,一些人际关系技巧,一些写作技巧(我觉得他说的很实在)。在谈及这么多年技术生涯的感想时,ken说,自十年前的互联网出现以来,计算机领域估计很难再出现新的爆炸式的理论了,于是他让他的儿子去学生物。他说,时代已经和他那时不同了,越来越少的人会去了解系统底层,编程已经变味儿了,整个世界充满了各种复杂的分层,所以“自己这个倔强的老头不愿去理解它们”(哎,No country for old man!)。Joshua说,如果可以重来的话,希望自己能够多学点数学知识(我现在也觉得要是以前多学点数学知识和哲学哲学就好了,这对构建和评价系统特别有帮助。),但是不管怎么样,他始终认为coding是这个星球上最有意思的工作,他始终觉得“我们长大在这个时代,是幸运的”。

  在书摘的最后,感想图灵出版社引入这本图书,也感谢辛苦工作的译者董金乾,武卫东,赫培强,朱巍。

  以上。

posted @ 2010-09-13 11:38 流浪de小F 阅读(662) 评论(3) 编辑 收藏

 回复 引用 查看   
#1楼2010-09-13 12:31 | 谢小漫      
书摘不错。支持一下。
 回复 引用 查看   
#2楼[楼主]2010-09-13 13:47 | 流浪de小F      
@谢小漫
呵呵,谢谢。

 回复 引用 查看   
#3楼2010-09-14 13:11 | 时永安      
楼主文笔不错,内容也很丰富有趣,支持一下。