代码大全读书心得

发布时间:2011-08-01 00:50:33

代码大全读书心得

还记得大学三年级的时候就在一位师兄那见到了《代码大全》,当时师兄就向我着力推荐,说它是一本值得收藏并精读的好书,它是一本百科全书式的帮助我们提高编程质量的优秀IT书籍,书中介绍了大量的提高代码质量的实实在在的技术和诀窍。但是由于种种原因,当时并未认真加以注意此书。颇有缘分的是,在此次民生银行的培训课程上,方总也着重推荐起了这本书。方总是JAVA界的重量级人物,什么书能值得他如此浓墨重彩地推荐,并要求不认真做读书笔记就不允许入职。想必此书必有过人之处,于是回来速度购买,并高度期待地阅读起来了。

经过1个月左右时间,通读此书下来,确实受益匪浅,感受颇多。此书英文名叫:code complete,诚如书中译序所说,这本书讲的正是为了到达“编码完成”这一重要里程碑所需的软件构建技术,确切地说,就是如何编写高质量的代码。高质量的代码既可以说是一个节省成本的问题,也可以说是一个软件安全性的问题。特别是针对金融行业的软件开发者而言,提高软件质量显得尤为重要。为了使我们能够编写出高质量的软件,书中讲述了软件构建的方方面面,详细讨论了源代码的可读性,类和函数命名、变量命名、数据类型和控制结构、代码布局等编程的基本要素,也讨论了防御式编程、表驱动法、协同构建、开发者测试、性能优化等有效的开发实践方法。同时书中展示了大量高质量的代码作为示例,同时也有低质量的代码来做比较。这对于已经有一定的编程基础,同时希望能够编写出更加高质量高水平的代码的我来说,无疑指明了努力的方向以及实践的标准指南。

本书章节甚多,此篇心得就不按照《项目经理案头手册》的心得那样做一个通篇心得阐述,而是就在阅读过程中有深刻体会的几个问题进行记录:

第一Defensive Programming 防御式编程

主要谈谈防止程序免遭非法输入数据的破坏和断言

防止程序被非法参数破坏已经是一个很古老的问题了,比较多遇到的是SQL注入。我觉得在防御非法数据方面Discuz!系列的产品都做得不错,或者说是开源的一些Web软件。似乎很少报出可利用的注入漏洞。反而是商业软件却经常有内存溢出等被利用的问题。

本节的代码大全指出,通常有三种方法来处理进来垃圾的情况:

1. 检查所有来源于外部的数据的值

2. 检查子程序所有输入参数的值

3. 决定如何处理错误的输入数据

在我写程序的时候我比较注意对外部数据的检查,但对于内部数据却总是疏于校验。防御式编程是提高软件质量技术的有益辅助手段。使用迭代式设计、编码前先写伪代码、写代码前先写测试用例及底层设计检查等活动,都有助于防止引入错误。

 

Today’s Topic 2: Assertions(8.2

今日主题2:断言(8.2

断言是什么?

所谓的断言就是指开发期间使用的、让程序运行时进行自检的代码。通俗的说就是一个过程或者一个函数是否达到预期目的,达到了预期目的就返回True否则就是False。今天阅读之前并不知道什么是断言,今天算是给了个定义。对于大型的复杂程序或者可靠性要求较高的程序来说,断言是非常重要的,它能帮助程序员排查出错误。

Building Your Own Assertion Mechanism 建立你自己的断言机制

就我目前所学习过的语言来说基本上都支持断言,如:VBPHPC++等。

Guidelines for Using Assertions 使用断言的指导建议

1. 用错误处理代码处理预期会发生的情况,用断言来处理绝对不应该发生的情况。

2. 不要把需要执行的代码放到断言中。(编译器可能会将这些代码排除在外而不去编译它)

3. 用断言来注释并验证前后条件

4. 应当先使用断言在进行错误处理

第二 高质量的子程序

http://life.dabai.org/2008/04/03/232.dabai

第三 变量名的力量

名不正则言不顺

“为变量命名”恐怕是编程中最普通的一项活动,一般介绍编程风格的书都会用几页的篇幅给出一些好的建议[KP99, Section 1.1],而《代码大全》用了整整一章30多页的篇幅(第11章)来讨论变量的命名,另外第7.3节专门讨论子程序的命名,第6.2节讨论了类的命名。如果变量、子程序和类型命名得当,代码本身就能用作程序的文档,可以减少注释和外部文档(第32.2节)。

变量名

为变量命名时最重要的考虑事项是,该名字要完全、准确地描述出该变量所代表的事物。currentDatetodaysDate都是很好的名字,因为它们都完全而且准确地描述出了“当前日期”这一概念。事实上,这两个名字都用了非常直白的词。程序员们有时候会忽视这些普通词语,而它们往往却是最明确的。cdc是很糟的命名,因为它们太短,同时又不具有描述性。current也很糟,因为它并没有告诉你是当前的什么。date看上去不错,但经过最后推敲它也只是个坏名字,因为这里所说的日期并不是所有的日期均可,而只是特指当前日期;而date本身并未表达出这层含义。xx1x2永远是坏名字——传统上用x代表一个未知量;如果你不希望你的变量所代表的是一个未知量,那么请考虑取一个更好的名字吧。

名字应该尽可能地明确。像xtempi这些名字都泛泛得可以用于多种目的,它们并没有像应该的那样提供足够信息,因此通常都是命名上的败笔。有人也许会反驳说,把i用作循环下标是最正常不过的了,难道非得写成indexOfTheLoop这种又臭又长的名字才算好吗?

Steve McConnell认为,如果循环只有寥寥数行,而且只是单层循环,那么用i是也是可行的。不过试想一下,如果你一直习惯用i作循环下标,而你将来可能需要把这个循环放到另一个循环中去执行,即循环嵌套,那么内外层循环都用i作下标肯定是不行的。如果编译器提醒你说变量i重复定义,那还算走运;如果编译器默不作声,而你自己又忘了修改,呃,你听见虫子飞舞的声音了吗?由于代码会经常修改、扩充,或者复制到其他程序中去,因此很多有经验的程序员索性不使用类似于i这样的名字。

如果循环不是只有几行,那么代码阅读者会很容易忘记i本来具有的含义,因此最好给循环下标换一个更有意义的名字。导致循环变长的常见原因之一是出现循环的嵌套使用。如果你使用了多个嵌套的循环,那么就应该给循环变量赋予更长的名字以提高可读性:

for (int teamIndex = 0; teamIndex < teamCount; teamIndex++){

   for (int eventIndex = 0; eventIndex < eventCount[teamIndex]; eventIndex++){

     score[teamIndex][eventIndex] = 0;

   }

}

谨慎地为循环下标变量命名可以避免产生常见的下标串话(index cross-talk)问题:想用j的时候写了i,想用i的时候却写了j。同时这也使得数据访问变得更加清晰:score[teamIndex][eventIndex]要比score[i][j]给出的信息更多。

如果你一定要用ijk,那么不要把它们用于简单循环的循环下标之外的任何场合——这种传统已经太深入人心了,一旦违背该原则,将这些变量用于其他用途就可能造成误解。要想避免出现这样的问题,最简单的方法就是想出一个比ijk更具描述性的名字来。

变量名的最佳长度似乎应该介于xmaximumNumberOfPointsInModernOlympics之间。太短的名字无法传达足够的信息。诸如x1x2这样的名字所存在的问题是,即使你知道了x代表什么,也无法获知x1x2之间的关系。太长的名字很难写,同时也会使得程序的视觉结构变得模糊不清。

研究发现,当变量名的平均长度在1016个字符的时候,调试程序所需花费的气力是最小的。平均名字长度在820个字符的程序也几乎同样容易调试。这项原则并不意味着你应该尽量把变量名的长度控制在915或者1016个字符。它强调的是,如果你查看自己写的代码时发现了很多更短的名字,那么需要认真检查,确保这些名字含义足够清晰。

子程序名

好的子程序名字能清晰地描述子程序所做的一切。《代码大全》第7.3节列举并详细说明了若干条指导原则:描述子程序所做的所有事情,避免使用无意义的、模糊或表述不清的动词,不要仅通过数字来形成不同的子程序名字,根据需要确定子程序名字的长度,给函数命名时要对返回值有所描述,给过程起名时使用语气强烈的动词加宾语[3]的形式,准确使用对仗词,为常用操作确立命名规则等。

类名

类的名称应该表达了其中心目的,准确的描述该类的接口所模塑的抽象概念(第5.3节),一般用名词。

无论如何,命名不是一锤子买卖,一旦发现有更好的名称,借助现代的IDE工具(EclipseVisual Studio 2005),我们很容易对变量、常量、类、子程序进行重命名(rename),这恐怕也是用得最多的一项重构操作了。

第四格式与规范

《代码大全》第31章专门介绍代码的布局与风格,前面提到过,编码规范最有用之处在于让你避免做出武断决定,避免把时间花在无谓的争执上(第34.5节)。McConnell并不像一位“家具警察”那样对待代码的格式,他认为好的代码布局应凸现程序的逻辑结构,使代码易于阅读、理解、检查及修改。至于循环体应该缩进几个空格,大括号的摆放位置这些问题,正确答案不止一种。每次回答同样内容比起只是回答正确更重要。

28.5节谈到了程序员的信仰问题,缩进风格、大括号的摆放位置、注释风格、命名习惯、对goto的使用、对全局变量的使用等等都是十分敏感的话题。关于这种问题,我觉得Herb SutterAndrei Alexandrescu的观点更贴近程序员的想法[SA04, Item 0]

那些“仅仅是个人品味、而不影响正确性或可读性的”议题不应出现在编码标准中。任何一个专业的程序员都应该能轻易地阅读并编写“那种格式与自己的习惯略有不同的”代码。

每个源文件(甚至每个项目)内确保采用一致的编排格式,因为在同一块代码中切换若干种风格是很不和谐的。但是不要试图对多个项目(甚至对整个公司)强制使用相同的编排格式。

……重要的不是设定格式规则,而仅仅是与“你维护的文件中已经采用的”格式保持一致。  

  ■    不要指明缩进多少字符,但缩进要显出结构:你愿意用多少个空格来缩排都行,但至少每个文件保持一致。

  ■    不要规定每行的长度,但确保可读性:你愿意每行多长就多长,只要别太过分就行。研究表明,最适合人眼阅读的情况是每行不超过10个单词。

  ■    不要规定注释的风格(某些工具将特定风格的注释提取为文档的情况除外),但一定编写有用的注释:只要有可能,尽量以代码代替注解。不要编写与代码重复的注解;这些注解会逐渐变得与代码不同步。一定编写说明性的注解,以解释所用的方法和基本原理。

……

关于大括号的摆放,以下数种做法在可读性上没有区别:

void using_k_and_r_style() {

    // ...

}

 

void putting_each_brace_on_its_own_line()

{

    // ...

}

 

void or_putting_each_brace_on_its_own_line_indented()

    {

    // ...

    }

每个专业的程序员都能毫无困难地阅读并编写以上任何一种风格的代码。但一定要保持一致:不要随意放置大括号,不要让作用域的嵌套关系变得混淆,并尽量遵循各个文件已有的风格。

如果你已经知道什么样的代码才是高质量的,那么怎样才能编写出这种代码呢?McConnell认为,好习惯很重要,因为程序员做的大部分事情都是无意识完成的(第33.9节)。例如,你曾想过该如何格式化缩进的循环体,但现在每当写新的循环体时就不再去想了,而以习惯的方式来做。对程序格式的方方面面几乎都是如此。你上次质疑编排风格是什么时候?如果你有五年编程经验,最后一次提出这个问题多半是在四年半之前,其余时间都是按习惯编程的。Bill Gates说过,任何日后出色的程序员在入行的前几年就做得很好,从那以后,程序员的优劣就定型了。其实任何行当都是如此,因此在初涉编程时,就应端正态度来学,尽快培养良好的习惯。

第五 关键的“构建”决策

4.1选择编程语言。 



程序员使用熟悉的编程语言生产率高于使用不熟悉的编程语言,并且高级语言比低级语言的效率要高得多,作者列出了几种高级语言的每一行代码与等效的c语言代码行数之比。比如java2.5,而perlpython就是6.而且编程语言影响程序员的思维随处可见,作者举了个他遇到的项目,那就是一群fortran程序员去写c++,最后写出来的代码就是伪装成c++fortran代码。 



这个很有感触,自己第一个项目当时有用到c,而当时项目组里面只有一个人对c比较熟悉,可是没办法,人不够,只好上我们几个做java的,最后开发效率非常低下,而且代码写的及其丑陋,问题一大堆,就一个c里面的字符串,把我们几个java程序员就给郁闷了好久。 



说道表达能力,象pythonperlruby之类的脚本语言是c或者java之类的不能比的,就像上面作者给出的统计,一行脚本语言比一行c或者java代码要完成的工作多得多,所以很多游戏都会采用脚本作为一个辅助。 

上次看到一篇文章就是讲java的标准库里面的一段c风格的java代码: 

我觉得特别是这种有gc的语言的程序员和无gc的语言的程序员,如果让他们互换的话,写出来的程序是最会不伦不类的

4.2 编程约定 



其实很简单,那就是变量名称,类的名称,方法名称,代码的格式,注释的规定等等等等,这些很细节的东西。程序因此也需要底层的完整性。成功编程的一个关键就是避免随意的变化。 



这些我觉得在项目中很重要,比如方法的名称,在我的一个项目中,那些c代码中的名称,有的是c的命名规则,有的是java的命名规则,所以这些代码给人感觉极度混乱和邋遢。 



4.3深入一种语言去编程。 



作者给出了建议,那就是如果你使用的语言缺乏你所希望用的构件,或者倾向于出现其他种类的问题,那就应该去试着弥补他,发明你自己的编码约定,标准,类库以及其他的改进措施。 



作者这里给出了一个vb的例子,其实像apache commons那几个包,或者说像springhibernate这些框架也都是深入java去编程的很好的例子。 

这是第一次接触到软件构建的概念,以前开发一个软件是完全按照软件工程的步骤来区分不同的活动。从定义问题,需求分析……到系统测试及其保障维护。

本书定义了什么是软件构建、软件构建的重要性、软件构建必须要做的准备工作。

软件构建主要是编码和调试,但也涉及详细设计、规划构建、单元测试、集成、集成测试等其他活动。

研究表明:把主要精力集中在构建活动,可以大大地提高程序员的生产率;而且构建活动的质量对软件的质量有着实质性的影响。

这就要求我们能够在软件的开发过程中,心中得有一个构建的概念,同时牢牢住住这个软件开发的核心活动,以高效率地完成高质量的软件开发任务。

然后在第三章中描述了软件构建必须做的准备工作。包括问题定义、需求确定、设计架构等等。书中说“就像修建建筑物一样,项目成败很大程序上在构建活动开始之前就已经注定了。”

如果没有明确的问题定义,那么你可能会在构建期间解决错误的问题。

如果没有做完良好的需求分析工作,你可能没能察觉待解决的问题的重要细节。

如果没有做完良好的架构设计,你可能会在构建期间用错误的方法解决正确的问题。

以上三点看起来谁都能理解其重要性,但是在实际的开发过程中却经常做不到。记得以前给人开发一个论坛的项目,由于自己时间紧迫,而直接就根据以往的经验开发了一个论坛给别人使用,但是最终交接的时候却发现很多细节的地方都不能满足客户的需求,以至于项目不得不返工而浪费了更多的时间。

至于什么算是一份明确的需求,什么算是一个好的架构。在书中提供了相应的checkList供我们参考。

第二部分 创建高质量的代码

以上是本人在第一次通读《代码大全》书籍过程中的一些个人体会,全书中还有很多的地方值得好好阅读和学习的,对于软件编程人员来说都有很大帮助,以后会将《代码大全》作为案头书籍之一,继续坚持学习代码大全的思想,必将受益颇多。

代码大全读书心得

相关推荐