读《代码大全》书评
2010-03-08
《代码大全》是相当经典的一本书,经典到软件项目开发的任何角色都有必要好好地读一读它。
应网友请求,写一篇书评,于是我再读一遍,查漏补缺。
大概内容
书的前几章,相当经典。作者从大量的图表、数据证明了软件需求分析、概要设计等“前期准备活动”的重要性。书里把编写软件的过程,以盖房子(第2章)为类比。前期准备妥当,房子一点点儿盖起来,最好不要出差错,最后就是一座好房子。但是,如果一旦哪座墙盖坏了,要拆掉重盖,那损失的就不仅是那些砖头与钉子,而是大量的人力的损失,而且拆掉它也是又费功夫又费力的。
后一章,又把软件开发的各个时期,比喻为食物链。这个比方甚妙,它非常形象地说明了一个软件项目各个时期的每个角色应当做的事情。其实这个比方,我认为还说明了软件模型中的分层的含义。现在很多程序员写的程序,不分层的,数据的处理与逻辑的处理混在一起,高度契合,谁想插入一点功能,也得把整个系统的数据流程搞个底儿朝上而且都不一定搞得了,它们以此来打压那些妄想修改自己写的软件的人的信心,提高自己的威信力,使自己始终处于在团队中的最高水平程序员的位置。引申一点,好的软件项目,任何一个层次,严格分好,像食物链似的,每一层完全消化掉下一层,再上一层不关心下一层,最好。
第4章中再一次老生常谈了“programming in a language”和“Programming into a language"的区别。gawk的作者,在它的《实战Linxu编程精髓》的末尾,也列了一篇“十年学会编程”,讲得非常有道理。在一种语言上编程和深入一种语言去编程,真得是高水平程序员与垃圾程序员的两种境界。
第5章就详细地讲述了如何在软件编程中构建方面的问题,它的每一个字都值得认真地分析一下,它把我们平常的大量代码编写后得出来的结论,做了不错的总结。比如,如何划分一个类,如何区分类的信息是不是隐藏,等等。而第6章,则马上跟进这一主题,讲述了类的设计中的诸多细节问题。不过有点可惜的是,这一章的内容需要一些C++的功底,对于喜欢如Lisp、Haskell等函数式编程语言的人来说,本章可能不太容易看懂,并且稍微有点儿让人摸不着头脑的感觉。这可能也应该算作是本书的一个小缺点,如果作者能够像Kunth大牛那样,自己“发明”一种新的语言来讲道理的话,就太好了。
第7章讲的是子过程的诸多细节,基本上渗透到了子程序设计中的各个方面;第8章讲防御式编程,举了防御式驾驶的例子,即你为了自己的安全,不能假定别的司机都是正常的。这章讲了遇到错误的各种处理方法,对于思考我们的错误处理问题,很有帮助。我们需要在碰到不同问题的时候,用不同的解决方案,而不能一概而论。
第9章讲述的伪代码的问题,我深有体会。以前我喜欢“拼凑”子程序,想好子程序的功能,马上开始写代码,写完测试,然后找bug。后来我在写一个加密的算法过程中,先把算法详细地用伪码描述好,然后一点一点地把伪码翻译成代码,很快就完成了我认为原来2到3倍的时间才能完成的功能,并且没有出现问题。从此,我每遇到复杂一些的功能实现,都先仔细地把过程理一遍,不管是程序中,还是脑海中,把伪码一行一行地写好,再慢慢翻译。
下面的几章,都是讲的变量的相关问题。比如,变量什么时候初使化以及要不要初使化。作者的意图跟我们的一致,即尽量在使用的前面声明,尽量在使用的前面进行初使化,我很欣慰。后面的大量的关于变量名的起名原则的介绍,都相当地有道理,如何命名数值变量,如何命名攻举变量,如何命名状态标志等等细节,作者都进行了比较详细的分析。这些成果,对我们书写大型的软件项目而言,都比较重要。
第12章开始数据类型相关的细节,再次提醒我,注意浮点数的精度,把一列浮点数按从小到大排好序再相加可以得到更好的精度,我发现这一条我又忘记了。而且这一章你再一次支持了我关于在递归层次不深的函数中时使用字符串变量时,尽量不要申请内存而是直接用临时变量的“真理”。我认为见个字符串的操作就弄个length + 1的长度的动态空间,简直就是没事儿吃饱了撑的的型儿。
下面几章,一步一步地介绍了编程语句中的一些规则,如if、case、loop等需要注意些什么,控制些什么,读者应该一边看,一边检查自己的编程习惯,看看是否对自己有所帮助。第17章还详细讨论了有关goto语句的优劣分析,这块历史被作者讲得绘声绘色,还插入了Kunth对这一问题的看法。但是,作者狡猾地没有给出最终意见,只是做了总结性地分析,怎么用,还得读者自己掌握。
从第20章开始,《代码大全》开始有一点偏离了主旨(当然不是真的),讲起了软件开发质量。同样又举出了大量的实例,论述了软件开发中合作与并行开发的很多方面,以为质量保证相关的主题。这些对于任何人(不管是管理者,还是开发者本身)都很有意义。它们中的很多东西,可以与《人月神话》对照着看看,很有意思,增强理解,呵呵。在第23章,作者还精确地列举了一些“迷信式编程”与“迷信式调试”的程序员的特征,如果你对比发现自己符合,一定要改正自己的态度。另外,本章中的很多调试技巧及经验,也是程序员们应该按条儿记住并且用于实践之中的。第26章列举了大量的代码优化方法,应该慢慢吸引,养成习惯。
第27章讲了程序规模对构建的影响。比较有意思的是,在本章中还有一个物理环境的主题,你可以把这个表撮出来,用于你向老板申请改善你的工作环境的论据。第28章关于管理构建的论述中,有一块儿再度引起了我这个不得志的年轻程序员的共鸣:程序员的开发质量或者生产率,与经验并没有什么关系。这十分鲜明地说明了“N年工作经验”的招聘条款是多么的无知而且可笑。要求程序员有多少多少年的工作经验,其实跟用结婚多少年来衡量他的性能力是一样的愚蠢,只不过是相当的HR大人们没有注意到这种相似性罢了(至少在中国是这样)。
第29章讲集成。我最喜欢关于daily build和冒烟测试的介绍,这是我最喜欢的一种集成模式。我也希望,所有的开发者,对于将要提交的代码,走一遍build,做一下冒烟测试。这样,不仅能发现bug,更重要的是在团队合作中的一种良好的提交代码的习惯。
读者应该认真地读一下第30章的核对表,看看自己编程中用到的环境是否满足书中所列的条件。如果不满足,建议仔细打造一下。功欲善其事,必先利其器,磨刀不误砍柴功嘛!
后面一章说的是程序员在实际的编程中的风格与布局的问题。作者对各种编程中的元素的布局都进行详细地分析,值得我们每一个代码工匠好好地看看,武装自己。但一些纯粹的(与可读性、可维护性无关)的风格问题,作者同样狡猾地没有给出统一的标准。我的观点是,个人要自由,但团队要标准。你可以按任何自己喜欢的风格书写自己的程序,但请在提交给项目组之前,按照项目组的要求,indent(Unix族系统的源代码格式化工具)一下。但是,如果项目组没有标准(这首先是项目管理者的疏误,你应该把意见告诉项目管理者)你就不能强行要求别的成员按你的要求格式化源代码。如果你不喜欢别人的风格,读人家的程序之前,就在本地indent出一份自己喜欢的,但不要改变人家的原始版本。我读过很多开源的代码,它们的风格各异,并没有发现哪一种对我的理解力造成了伤害或者说出乎意料的提升,它们的风格我都喜欢。但是我自己写程序,我喜欢GNU的格式,仅管它们不太为“标准”所容。
最后三章基本上算是个总结,但从另外的角度讲述了一个程序员(或者学生)如何成长为一个编程大师。首先是性格及素质,然后是自己的代码要注意的方方面面,最后一章则提供了一个详细的修炼到大师的清单。我很脸红地发现,提到的大部分网址,在下从来没有登陆过一次,提到的书本资料,读过的也没有几本儿。
看来自己找到还没有成为大师的原因了!
书中的趣语不完全摘录
书中有很多语句很有幽默感,下面是我在读的过程当中笑中声来的一些句子。
* 程序员不做准备工作的最后一个原因是,管理者们对那些“花时间进行构建活动的前期准备的程序“的冷漠已经到了人神共愤的程度。
* 对于显示癌症病人X光片的绘图子程序而言,最好还是不要显示某个“中立值”。在这种情况下,关闭程序也比让它显示错误的病人数据要好。
* 编程爱好者和专业程序员之间的最大区别之一便是从迷信到理解的转变。这里所说的“迷信”并不是指一个程序会在月圆之夜时让你心生惊恐或者产生什么莫名其妙的错误。指的是把对代码的感觉当做对它的理解。
* 避免使用你男朋友的名字、妻子的名字、最喜欢的啤酒的名字或者其它自做聪明的(也就是傻的)名字来为变量命名,除非你的程序真的是与你的男朋友、妻子或者最爱的啤酒有关。即使如此,你也应该明智的认识到这其中的每一项都可能会变的,所以boyfriend、wife和favoriteBeer这些通用的名字会更好。
* 这时,你可以把下面的原则看做是在出游陌生国度前注射的疫苗,它们在某种程度上或许会带来些痛苦,但可以让你在旅行中更加健康。
* 撒旦已慷慨地将地狱的某个部分租给那些在调试时怨天犹人的程序员了。每个团队里都也许有这样一个程序员,他总会遇到无穷的问题:不听话的机器,奇怪的编译器错误,月圆时才会出现的编程语言的隐藏缺陷,失效的数据,忘记做的重要改动,一个不能正常保存程序的疯狂的编辑器——你怎么描述这种行为好呢?这就是“迷信式编程”。
* 如果你正在手写一本书,那么是不会把手稿堆在走廊里的。不然的话,书稿可能被雨淋湿,被风吹走,或者被你邻居家的小狗借回去垫窝。
问题
《代码大全》也有一些问题,可能作者不想写太深了以至于对读者造成理解障碍吧。有一些问题,还是比较明显的。这里也摘录几个:
第4章开头介绍各种编程语言时,说“Perl是一种处理字符串的语言,基于C和各种UNIX工具的程序”。我以玉皇大帝的名义证明,Perl的功能决不仅仅是“一种处理字符串的语言”。虽然Perl官方的说法也是“90%处理文本,另外10%处理其它事情”,并且Perl对字符串的处理确实是有自己的独到之处。但是,现在的Perl的能力,已经远远超过了这些。至少,我就用Perl写过不少GUI的界面程序,并且在公司也编写并且维护着纯用Perl 完成的网络性能及排错的测试脚本。Larry的孩子Lewis,在维护着一个用Perl做的游戏引擎。
第12章第2节说到,“在编译器设法弄清了应该用什么类型去进行比较之后,它会把其中一种类型转化为另一种,执行一些四舍五入运算之后才得出结果 ”。在C语言中,都是从表示范围从低向高转化,符号整形转化为整形,整形转化为浮点数,即使要把其它值强制转换成整形的话,也是直接去尾,不会有四舍五入这种情况的。这里真的是要进行四舍五入吗?我非常怀疑这一点,但是还没有找出证据。
原文连接:http://blog.chinaunix.net/u/19628/showart.php?id=2193127