Logo

代码大全(第2版)

代码大全(第2版)

ISBN:9787121022982

作者:[美] 史蒂夫·迈克康奈尔

译者:金戈 / 汤凌 / 陈硕 / 张菲 译 / 裘宗燕 审校

出版社: 电子工业出版社

出版时间:2006-3

评价:☆☆☆☆☆

《代码大全》并不是写满了各种代码来给你使用的那种书,它主要讲述的是项目中最重要的构建部分。如果你是程序员或者想当程序员,一定要看这本书。看过这本书后,你会觉得为什么自己之前没有看这本书,它是一本让人相见恨晚的书。你需要反复阅读很多次,它会陪伴你从初级程序员到高级程序员、专家。每重读一边你会惊奇的发现,该书也和你一起成长了。当然书是死的,它并不能真正的成长,会有这种感觉是因为你的理解力提升后,再次阅读就能理解到以前没有理解到的东西。

软件开发的主要问题的管理复杂度,其解决方案是良好的需求、设计、构建以及测试。本书主要涉及构建,作者提出使用良好的编程习惯、先写伪代码再编码和不断的迭代开发(重构、重写、增量开发)来分解软件的复杂度。书中有许多建议在实践之后非常有效。例如:验证输入(第8章 防御式编程);先编写伪代码,然后将伪代码改为注释和代码 (第9章 伪代码编程过程);重构(第24章 重构,本身中的重构只是讲述了一些简单和常见的重构方法,如果你需要深入了解重构的话,则需要去读一读《重构》)。

配套网站:www.cc2e.com

参考书籍列表

第1部分 打好基础(Laying the Foundation)

第1章 欢迎进入软件构建的世界(Welcome to Software Construction)

构建活动是唯一一项确保会完成的工作

一个理想的软件项目在进行构建之前,都要经过谨慎的需求分析和架构设计。一个理想的项目在构建完成之后,也要经历全面的、统计意义上受控制(statistically controlled)的系统测试。然而现实中不那么完美的软件项目,往往跳过需求和设计的阶段而直接跃入构建环节。之后又由于有太多的错误要修正而时间又不够。测试环节也被抛到一边了。但是,无论一个项目的计划有多匆忙、多糟糕,它都不可能扔下构建活动——这是不可或缺的环节。因此,对构建活动进行改进,是改进软件开发过程的一种有效途径。

要点

  • 软件构建是软件开发的核心活动;构建活动是每个项目中唯一一项必不可少的工作。
  • 软件构建是主要活动包括:详细设计、编码、调试、集成、开发者测试(developer testing)(包括单元测试和集成测试)。
  • 构建也常被称作“编码”和“编程”。
  • 构建活动的质量对软件的质量有着实质性的影响。
  • 最后,你对“如何进行构建”的理解程度,决定了你这名程序员的优秀程度——这就是本书其余部分的主题了。

第2章 用隐喻来更充分地理解软件开发(Metaphors for a Richer Understanding of Software Development)

重要的研发成果常常产自类比(analogy)。通过把你不太理解的东西和一些你较为理解、且十分类似的东西做比较,你可以对这些不太理解的东西产生更深刻的理解。这种使用隐喻的方法叫做“建模(modeling)”。

隐喻的价值绝不应低估。隐喻的优点在于其可预期的效果:能被所有的人理解。不必要的沟通和误解也因此大为减低,学习和教授更为快速。实际上,隐喻是对概念进行内在化(Internalization)和抽象(abstracting)的一种途径,它让人面子更高的层面上思考问题,从而避免低层次的错误。——Fernando J. Corbato

要点

  • 隐喻是启示而不是算法。因此它们往往有一点随意(sloppy)。
  • 隐喻把软件开发过程与其他你熟悉的活动联系在一起,帮助你更好地理解。
  • 有些隐喻比其他一些隐喻更贴切。
  • 通过把软件的构建过程比作房屋的建设过程,我们可以发现,仔细的准备是必要的,而大型项目和小型项目之间也是有差异的。
  • 通过把软件开发中的实践比作是智慧工具箱中的工具,我们又发现,每位程序员都有许多工具,但并不存在任何一个能适用于所有工作的工具,因地制宜地选择正确工具是成为能有效编程的程序员的关键。
  • 不同的隐喻彼此并不排斥,应当适用对你最有益处的某种隐喻组合。

第3章 三思而后行:前期准备(Measure Twice, Cut Once: Upstream Prerequisites)

如果你在项目的末期强调质量,那么你会强调系统测试。当提到软件质量保证的时候,许多人都会想到测试。但是测试只是完整的质量保证策略的一部分,而且不是最有影响的部分。测试是不可能检查出诸如“制造了一个错误的产品”,或者“使用错误的方法制造正确的产品”之类的缺陷的。这样的缺陷必须在测试之前解决——更确切地说是在构建活动之前。

要点

  • 构建活动的准备工作的根本目标在于降低风险。要确认你的准备活动是在降低风险,而非增加风险。
  • 如果你想开发高质量的软件,软件开发过程必须由始至终关注质量。在项目初期关注质量,对产品质量的正面影响比在项目末期关注质量的影响要大。
  • 程序员的一部分工作是教育老板和合作者,告诉他们软件开发过程,包括在开始编程之前进行充分准备的重要性。
  • 你所从事的软件项目的类型对构建活动的前期准备有重大影响——许多项目应该是高度迭代的,某些应该是序列式的。
  • 如果没有明确的问题定义,那么你可能会在构建期间解决错误的问题。
  • 如果没有做完良好的需求分析工作,你可能没能察觉待解决的问题的重要细节。如果需求变更发生在构建之后的阶段,其代价是“在项目早期更改需求”的20至100倍。因此在开始编程之前,你要确认“需求”已经到位了。
  • 如果没有做完良好的架构设计,你可能会在构建期间用错误的方法解决正确的问题。架构变更的代价随着“为错误的架构编写的代码数量”增加而增加,因此,也要确认“架构”已经到位了。
  • 理解项目的前期准备所采用的方法,并相应地选择构建方法。

第4章 关键的“构建”决策 (Key Construction Decisions)

编程语言影响程序员的思维的证据随处可见。典型的故事类似下面的样子:“我们用C++编写一个新系统,但是大多数程序员没有太多C++经验。他们具有Fortran语言背景。他们编写出能用C++编译的代码,但实际上编写的是伪装成C++的Fortran代码。他们扭曲C++来模拟Fortran的不良特性(例如goto语句和全局数据)并且忽略了C++丰富的面向对象能力。”这种想象多年来在整个行业当中随处可见。

要点

  • 每种编程语言都有其优点和弱点。要知道你使用的语言的明确优点和弱点。
  • 在开始编程之前,做好一些约定(convention)。“改变代码使之符合这些约定”是近乎不可能的。
  • “构建的实践方法”的种类比任何单个项目能用到的要多。有意识地选择最适合你的项目的实践方法。
  • 问问你自己,你采用的编程实践是对你所用的编程语言的正确响应,还是受它的控制?请记得“深入一种语言区编程”,不要仅“在一种语言上编程”。
  • 你在技术浪潮中的位置决定了哪种方法是有效的——甚至是可能用到的。确定你在技术浪潮中的位置,并相应调整计划和预期目标。

 

第2部分 创建高质量的代码(Creating High-Quality Code)

第5章 软件构建中的设计(Design in Construction)

在小型的、非正式的项目里,很多的设计工作的程序员坐在键盘前完成的。这里的“设计”可能就是指在编写具体代码之前先用伪代码写出一个类的接口,也可能就是编码之前画出几个类之间的关系图,还可能就是询问另一位程序员用哪种设计模式会更好。无论是以何种方式来进行设计,小型项目也能和大型项目一样从精心的设计中获益,而如果能认识到设计是一项明确的活动,你就更会获益匪浅。

如果有位老师给你一份编程作业,你刚完成设计时他就把作业要求改了,然后就在你将要提交完整的程序时,他又对作业的要求再次改动,这时你肯定会十分生气,然而这一过程正是在专业编程中每日可见的真实情形。

有两种设计软件的方式:一种方法是让设计非常简单,看上去明显没有缺陷;另一种方法是让设计非常复杂,看上去没有明显的缺陷。——C. A. R Hoare

增量式地改进是一种管理复杂度的强大工具。正如Polya在数学问题求解中所建议的那样——理解问题、形成计划、执行计划,而后再回顾你的做法。

要点

  • 软件的首要技术使命就是管理复杂度。以简单性作为努力目标的设计方案对此最有帮助。
  • 简单性可以通过两种方式来获取:一是减少在同一时间所关注的本质性复杂度的量,二是避免生成不必要的偶然的复杂度。
  • 设计是一种启发式的过程。固执于某一种单一方法会损害创新能力,从而损害你的程序。
  • 好的设计都是迭代的。你尝试设计的可能性越多,你的最终设计方案就会变得越好。
  • 信息隐藏是个非常有价值的概念。通过询问“我应该隐藏些什么?”能够解决很多困难和设计问题。
  • 很多有用有趣的、关于设计的信息存在于本书之外。这里所给出的观点只是对这些有价值的资源的一点提示而已。

第6章 可以工作的类(Working Classes)

要点

  • 类的接口应提供一致的抽象。很多问题都是由于违背该原则而引起的。
  • 类的接口应隐藏一些信息——如某个系统接口、某项设计决策、或一些实现细节。
  • 包含往往比继承更为可取——除非你要对“是一个(is a)”的关系建模。
  • 继承是一种有用的工具,但它却会增加复杂度,这有违与软件的首要技术使命——管理复杂度。
  • 类是管理复杂度的首选工具。要在设计类时给予足够的关注,才能实现这一目标。

第7章 高质量的子程序(High-Quality Routines)

要点

  • 创建子程序最主要的目的是提高程序的可管理性,当然也有其他一些好的理由。其中,节省代码空间只是一个次要原因;提高可读性、可靠性和可修改性等原因都更重要一些。
  • 有时候,把一些简单的操作写成独立的子程序也非常有价值。
  • 子程序可以按照其内聚性分为很多类,而你应该让大多数子程序具有功能上的内聚性,这是最佳的一种内聚性。
  • 子程序的名字是它的质量的指示器。如果名字糟糕但恰如其分,那就说明这个子程序设计得很差劲。如果名字糟糕而且又不准确,那么它就反应不出程序是干什么的。不管怎样,糟糕的名字都意味着程序需要修改。
  • 只有在某个子程序的主要目的是返回由其名字所描述的特定结果时,才应该使用函数。
  • 细心的程序员会非常谨慎地使用宏,而且只在万不得已时才用。

第8章 防御式编程(Defensive Programming)

防御式编程并不是说让你在编程时持“防备批判或攻击”的态度——“它就是这么工作!”这一概念来自防御式驾驶。在防御式驾驶中要建立这样一种思维,那就是你永远也不能确定另一位司机将要做什么。这样才能确保在其他人做出危险动作时你也不会受到伤害。你要承担起保护自己的责任,哪怕是其他司机犯的错误。防御式编程的主要思想是:子程序应该不因传入错误数据而被破坏,哪怕是由其他子程序产生的错误数据。更一般地说,其核心想法是要承认程序都会有问题,都需要被修改,聪明的程序员应该根据这一点来编程序。

要点

  • 最终产品代码中对错误的处理方式要比“垃圾进,垃圾出”复杂得多。
  • 防御式编程技术可以让错误更容易发现、更容易修改,并减少错误对产品代码的破坏。
  • 断言可以帮助人尽早发现错误,尤其是在大型系统和高可靠性的系统中,以及快速变化的代码中。
  • 关于如何处理错误输入的决策是一项关键的错误处理决策,也是一项关键的高层设计决策。
  • 异常提供了一种与代码正常流程角度不同的错误处理手段。如果留心使用异常,它可以成为程序员们知识工具箱中的一项有益补充,同时也应该在异常和其他错误处理手段之间进行权衡比较。
  • 针对产品代码的限制并不适用于开发中的软件。你可以利用这一优势在开发中添加有助于更快地排查错误的代码。

第9章 伪代码编程过程(The Pseudocode Programming Process)

创建一个类可以有多种不同的方式,但一般而言这都是一个迭代过程,先对一个类做总体设计,列出这个类内部的特定子程序,创建这些子程序,然后从整体上复查这个类的构建结果。

要点

  • 创建类和子程序通常都是一个迭代的过程。在创建子程序的过程中获得的认识常常会反过来影响类的设计。
  • 编写好的伪代码需要使用易懂的英语,要避免使用特定编程语言中才有的特性,同时要在意图的层面上写伪代码(即描述该做什么,而不是要怎么去做)。
  • 伪代码编程过程中是一个行之有效的做详细设计的工具,它同时让编码工作更容易。伪代码会直接转化为注释,从而确保了注释的准确度和实用性。
  • 不要只停留在你所想到的第一个设计方案上,反复使用伪代码做出多种方案,然后选出其中最佳的一种方案再开始编码。
  • 每一步完成后都要检查你的工作成果,还要鼓励其他人帮你来检查。这样你就会在投入精力最少的时候,用最低的成本发现错误。

 

第3部分 变量(Variables)

第10章 使用变量的一般事项(General Issues in Using Variables)

要点

  • 数据初始化过程很容易出错,所以请用本章描述的初始化方法来避免由于非预期的初始值而造成的错误。
  • 最小化每个变量的作用域。把同一变量的引用点集中在一起。把变量限定在子程序或类的范围之内。避免使用全局数据。
  • 把使用相同变量的语句尽可能集中在一起。
  • 早期绑定会减低灵活性,但有助于减小复杂度。晚期绑定可以增加灵活性,同时增加复杂度。
  • 把每个变量用于唯一的用途。

第11章 变量名的力量(The Power of Variable Names)

可以通过命名方面的约定来增强某种语言的功能,从而弥补该语言自身的缺陷。这种方式正是“深入一种语言去编程”而非仅仅“在一种语言上编程”的典范。

要点

  • 好的变量名是提高程序可读性的一项关键要素。对特殊种类的变量,比如循环下标和状态变量,需要加以特殊的考虑。
  • 名字要尽可能地具体。那些太模糊或者太通用以致于能够用于多种目的的名字通常都是很不好的。
  • 命名规则应该能够区分局部数据、类数据和全局数据。它们还应当可以区分类型名、具名常量、枚举类型名字和变量名。
  • 无论做哪种类型项目,你都应该采用某种变量命名规则。你所采用的规则的种类取决于你的程序的规模,以及项目成员的人数。
  • 现代编程语言很少需要用到缩写。如果你真的要使用缩写,请使用项目缩写词典或者标准前缀来帮助理解缩写。
  • 代码阅读的次数远远多于编写的次数。确保你所取的名字更侧重于阅读方便而不是编写方便。

第12章 基本数据类型(Fundamental Data Types)

要点

  • 使用特定的数据类型就意味着要记住适用于各个类型的很多独立的原则。用本章的核对表来确认你已经对常见问题作了考虑。
  • 如果你的语言支持,创建自定义类型会使得你的程序更容易修改,并更具有自描述性。
  • 当你用typedef或者等价方法创建了一个简单类型的时候,考虑是否更应该创建一个新的类。

第13章 不常见的数据类型(Unusual Data Types)

即使编程语言没有强制要求,限制对全局变量的访问也是一种很好的编程实践。这一实践正是“深入一种语言去编程”而非“在一种语言上编程”的极好例子。

要点

  • 结构体可以使得程序更简单、更容易理解,以及更容易维护。
  • 每当你打算使用结构体的时候,考虑采用类是不是会工作得更好。
  • 指针很容易出错。用访问器子程序或类以及防御式编程实践来保护自己的代码。
  • 避免用全局变量,不只是它们很危险,还是因为你可以用其他更好的方法来取代它们。
  • 如果你不得不使用全局变量,那么就通过访问器子程序来使用它。访问器子程序能为你带来全局变量所能带来的一切优点,还有一些额外好处。

 

第4部分 语句(Statements)

第14章 组织直线型代码(Organizing Straight-Line Code)

要点

  • 组织直线型代码的最主要原则是按照依赖关系进行排列。
  • 可以用好的子程序名、参数列表、注释,以及——如果代码足够重要——内务管理变量来让依赖关系变得更明显。
  • 如果代码之间没有顺序依赖关系,那就设法使相关的语句尽可能地接近。

第15章 使用条件语句(Using Conditionals)

要点

  • 对于简单的if-else语句,请注意if子句和else子句的顺序,特别是用它来处理大量错误的时候。要确认正常的情况是清晰的。
  • 对于if-then-else语句串和case语句,选择一种最有利于阅读的排序。
  • 为了捕捉错误,可以使用case语句中的default子句(默认子句),或者使用if-then-else语句串中的最后那个else子句。
  • 各种控制结构并不是生来平等的。请为代码的每个部分选用最适合的控制结构。

第16章 控制循环(Contrilling Loops)

你可以通过在头脑中模拟和手工运算而获益多多。这种智力训练带来的好处是:在最初的编码阶段少犯错误,在调试阶段更快地找出错误,以及从整体上更好地理解应用程序。它意味着:你能够真正的理解你的代码是如何工作的,而不是瞎猜。

要点

  • 循环很复杂。保存循环简单将有助于别人阅读你的代码。
  • 保持循环简单的技巧包括:避免使用怪异的循环、减少嵌套层次、让入口和出口一目了然、把内务操作代码放在一处。
  • 循环下标很容易被滥用。因此命名要准确,并且要把它们各自仅用于一个用途。
  • 仔细地考虑循环,确认它在每一种情况下都运行正常,并且在所有可能的条件下都能退出。

第17章 不常见的控制结构(Unusual Control Structures)

要点

  • 多个return可以增强子程序的可读性和可维护性,同时可以避免产生很深的嵌套逻辑。但是使用它的时候要多加小心。
  • 递归能够很优雅地解决一小部分问题。对它的使用也要加倍小心。
  • 在少数情况下,goto是编写可读性和可维护代码的最佳方法。但这种情况非常罕见。除非万不得已,不要使用goto。

第18章 表驱动法(Table-Driven Methods)

表驱动法是一种编程模式(scheme)——从表里面查找信息而不使用逻辑语句(if和case)。事实上,凡是能通过逻辑语句来选择的事物,都可以通过查表来选择。对简单的情况而言,使用逻辑语句更为容易和直白。但随着逻辑链的越来越复杂,查表法也就愈发显得更具吸引力。

要点

  • 表提供了一种复杂的逻辑和继承结构的替换方案。如果你发现自己对某个应用程序的逻辑或者继承树关系感到困惑,那么问问自己它是否可以通过一个查询表来加以简化。
  • 使用表的一项关键决策是决定如何去访问表。你可以采取直接访问、索引访问或者阶梯访问。
  • 使用表的另一项关键决策是决定应该把什么内容放入表中。

第19章 一般控制问题(General Control Issues)

要点

  • 使布尔表达式简单可读,将非常有助于提高你的代码的质量。
  • 深层次的嵌套使得子程序变得难以理解。所幸的是,你可以相对容易地避免这么做。
  • 结构化编程是一种简单并且仍然适用的思想:你可以通过把顺序、选择和循环三者组合起来而开发出任何程序。
  • 将复杂度降低到最低水平是编写高质量代码的关键。

 

第5部分 代码改善(Code Improvement)

第20章 软件质量概述(The Software-Quality Landscape)

要点

  • 开发高质量代码最终并没有要求你付出更多,只是你需要对资源进行重新分配,以低廉的成本来防止缺陷出现,从而避免代价高昂的修正工作。
  • 并非所有的质量保证目标都可以全部实现。明确哪些目标是你希望达到的,并就这些目标和团队成员进行沟通。
  • 没有任何一种错误检测方法能够解决全部问题,测试本身并不是排除错误的最有效方法。成功的质量保证计划应该使用多种不同的技术来检查各种不同类型的错误。
  • 在构建期间应当使用一些有效的质量保证技术,但在这之前,一些具有同样强大功能的质量保证技术也是必不可少的。错误发现越早,它与其他代码的纠缠就越少,由此造成的损失也越小。
  • 软件领域的质量保证是面向过程的。软件开发与制造业不一样,在这里并不存在会影响最终产品的重复的阶段,因此,最终产品的质量受到开发软件所用的过程的控制。

第21章 协同构建(Collaborative Construction)

“协同构建”包括结对编程、正式检查、非正式技术复查、文档阅读,以及其他让开发人员共同承担创建代码以及其他工作产品责任的技术。各种协同构建技术之间尽管存在着一些差异,但它们都基于一个相同的思想,那就是在工作中开发人员总会对某些错误点视而不见,而其他人不会有相同的盲点,所以开发人员让其他人来检查自己的工作是很有好处的。

要点

  • 协同开发实践往往能比测试发现更多的缺陷,并且更有效率。
  • 协同开发实践所发现错误的类型通常跟测试所发现的不同,这意味着你需要同时使用详查和测试来保证你软件的质量。
  • 正式检查通过运用核对表、准备工作、明确定义的角色以及对方法的持续改善,将缺陷侦测的效率提升至最高。它往往能比走查发现更多的缺陷。
  • 通常,结对编程拥有和详查相同的成本,并能产生质量相当的代码。当需要缩短开发周期的时候,结对编程就非常有价值。相对于单独工作来说,有些开发人员更喜欢结对工作。
  • 正式检查可以应用在除代码之外的很多工作成果上,例如需求、设计以及测试用例等。
  • 走查和代码阅读是详查的替代方案。代码阅读更富有弹性,能有效地利用每个人的时间。

第22章 开发者测试(Developer Testing)

与其他臭虫成灾的程序放在一起时,程序并不会像人感染细菌那样感染bug。Bug都是程序员自己引入的。——Harlan Mills

要点

  • 开发人员测试是完整测试策略的一个关键部分。独立测试也很重要,但这一主题超出了本书的范围。
  • 同编码之后编写测试用例相比较,编码开始之前编写测试用例,工作量和花费的时间差不多,但是后者可以缩短缺陷-侦测-调试-修正这一周期。
  • 即使考虑到了各种可用的测试手段,测试仍然只是良好软件质量计划的一部分。高质量的开发方法至少和测试一样重要,这包括尽可能减少需求和设计阶段的缺陷。在检测错误方面,协同开发的成效至少与测试相当。这些方法所检测错误的类型也各不相同。
  • 你可以根据各种不同的思路来产生很多测试用例,这些思路包括基础测试、数据流分析、边界分析、错误数据类型以及正确数据类型等。你还可以通过猜测错误的方式得到更多的测试用例。
  • 错误往往集中在少数几个容易出错的类和子程序上。找出这部分代码,从新设计和编写它们。
  • 测试数据本身出错的密度往往比被测代码还要高。查找这种错误完全是浪费时间,又不能对代码有所改善,因此测试数据里面的错误更加让人烦恼。要像写代码一样小心地开发测试用例,这样才能避免产生这种问题。
  • 自动化测试总体来说是很有用的,也是进行回归测试的基础。
  • 从长远来看,改善测试过程的最好办法就是将其规范化,并对其进行评估,然后从评估中获得的经验教训来改善这个过程。

第23章 调试(Debugging)

同测试一样,调试本身并不是改进代码质量的方法,而是诊断代码缺陷的一种方法。软件的质量必须从开始逐步建立:开发高质量软件产品的最佳途径是精确描述需求,完善设计,并使用高质量的代码编写规范。调试只是迫不得已时采用的手段。

调试其实是一片极其富饶的土地,它孕育着你进步的种子。这片土地也是所有软件构建之路所交织的地方:可读性、设计、软件质量,凡是你能想到的无所不包。编写优秀代码所得的回报:如果你能精于此道,你甚至无须频繁调试。

要点

  • 调试同整个软件开发的成败息息相关。最好的解决之道是使用本书中介绍的其他方法来避免缺陷的产生。然而,花点时间来提高自己的调试技能还是很划算的,因为优秀和拙劣的调试表现之间的差距至少是10:1.
  • 要想成功,系统化地查找和改正错误的方法至关重要。要专注于你的调试工作,让每一次测试都能让你朝着正确的方向前进一步。要使用科学的调试方法。
  • 在动手解决问题之前,要理解问题的根本。胡乱猜测错误的来源和随机修改将会让你的程序陷入比刚开始调试时更为糟糕的境地。
  • 将编辑器警告级别设置为最严格,把警告信息所报告的错误都改正。如果你忽略了明显的错误,那么要改正那些微妙的错误就会非常麻烦。
  • 调试工具对软件开发而言是强有力的支持手段。找出这些工具并加以应用,当然,请记得在调试的时候开动脑筋。

第24章 重构(Refactoring)

要点

  • 修改是程序一生都要面对的事情,不仅包括最初的开发阶段,还包括首次发布之后。
  • 在修改中软件的质量要么改进,要么恶化。软件演化的首要法则就是代码演化应当提升程序的内在质量。
  • 重构成功之关键在于程序员应学会关注那些标志着代码需要重构的众多的警告或“代码臭味”。
  • 重构成功的另一要素的程序员应当掌握大量特定的重构方法。
  • 重构成功的最后要点在于要有安全重构的策略。一些重构方法会比其他重构方法要好。
  • 开发阶段的重构是提升程序质量的最佳时机,因为你可以立刻让刚刚产生的改变梦想变成现实。请珍惜这些开发阶段的天赐良机!

第25章 代码调整策略(Code-Tuning Strategies)

性能同代码速度之间存在着很松散的联系。如果只是关注于代码的运行速度,你的工作不免顾此失彼。要特别当心放弃其他功能去让你的代码跑得更快。如果过分强调速度,程序的整体性能(表现)常常不升反降。

如果没有数据,任何程序员都无法预测或分析出性能瓶颈在哪儿。无论你猜测性能会如何变化,你都会惊奇地发现事实与你的设想背道而驰。——Joseph M. Newcomer

要点

  • 性能只是软件整体质量的一个方面,通常不是最重要的。精细的代码调整也只是实现整体性能的一种方法,通常也不是决定性的。相对于代码本身的效率而言,程序的架构、细节设计以及数据结构和算法选择对程序的运行速度和资源占用的影响通常会更大。
  • 定量测量是实现性能最优化的关键。定量测量需要找出能真正决定程序性能的部分,在修改之后,应当通过重复测量来明确修改是提高还是降低了软件的性能。
  • 绝大多数的程序都有那么一部分代码耗费了绝大部分的运行时间。如果没有测量,你不会知道是哪一部分代码。
  • 代码调整需要反复尝试,这样才能获得理想的性能提高。
  • 为性能优化工作做好准备的最佳方式就是在最初阶段编写清晰的代码,从而使代码在后续工作中易于理解和修改。

第26章 代码调整技术(Code-Tuning Techniques)

要点

  • 优化结果在不同的语言、编译器和环境下有很大差异。如果没有对每一次的优化进行测量,你将无法判断优化到底是帮助还是损害了这个程序。
  • 第一次优化通常不会是最好的。即使找到了效果很不错的,也不要停下扩大战果的步伐。
  • 代码调整这一话题有点类似于核能,富有争议,甚至会让人冲动。一些人认为代码调整损害了代码可读性和可维护性,他们绝对会将其弃之不用。其他人则认为只要有适当的安全保障,代码调整对程序是有益的。如果你决定使用本章所述的调整方法,请务必谨慎行事。

 

第6部分 系统考虑(System Considerations)

第27章 程序规模对构建的影响(How Program Size Affects Construction)

要点

  • 随着项目规模的扩大,交流需要加以支持。大多数方法论的关键点都在于减少交流中的问题,而一项方法论的存亡关键也应取决于它能否促进交流。
  • 在其他条件都相等的时候,大项目的生产率会低于小项目。
  • 在其他条件都相等的时候,大项目的每千行代码错误率会高于小项目。
  • 在小项目里的一些看起来“理当如此”的活动在大项目中必须仔细地计划。随着项目规模扩大,构建活动的主导地位逐渐降低。
  • 放大轻量级的方法论要好于缩小重量级的方法论。最有效的方法是使用“适量级”方法论。

第28章 管理构建(Managing Construction)

要点

  • 好的编码实践可以通过“贯彻标准”或者“使用更为灵活的方法”来达到。
  • 配置管理,如果应当得当,会使程序员的工作变得更加轻松。特别包括变更控制。
  • 好的软件评估是一项重大挑战。成功的关键包括采用多种方法、随着项目的开展而修缮评估结果,以及很好地利用数据来创建评估等。
  • 度量是构建管理成功的关键。你可以采取措施度量项目的任何方面,而这要比根本不度量好的多。准确的度量是制定准确的进度表、质量控制和改进开发过程的关键。
  • 程序员和管理人员都是人,在把他们当人看的时候工作得最好。

第29章 集成(Integration)

要点

  • 构建的先后次序和集成的步骤会影响设计、编码、测试各类的顺序。
  • 一个经过充分思考的集成顺序能减少测试的工作量,并使调试变容易。
  • 增量集成有若干变型,而且——除非项目是微不足道的——任何一种形式的增量集成都比阶段式集成好。
  • 针对每个特定的项目,最佳的集成步骤通常是自顶向下、自顶向上、风险导向及其他集成方法的某种组合。T-型集成和竖直分块集成通常都能工作得很好。
  • daily build能减少集成的问题,提升开发人员的士气,并提供非常有用的项目管理信息。

第30章 编程工具(Programming Tools)

始终需要人来填补真实世界里需要解决的问题和准备用来解决问题的计算机之间的鸿沟。这些人将会被称之为程序员,无论他是以汇编语言操控机器寄存器,还是用Microsoft Visual Basic操控对话框。只要是计算机,就需要能告诉计算机该去做什么的人,这一活动将会被称做编程。

要点

  • 程序员有时会在长达数年的时间里忽视某些最强大的工具,之后才发现并使用之。
  • 好的工具能让你的日子过得安逸得多。
  • 下面这些工具已经可用了:编辑、分析代码质量、重构、版本控制、除错、测试、代码调整。
  • 你能打造许多自己用的专用工具。
  • 好的工具能减少软件开发中最单调乏味的工具的量,但它不能消除对“编程”的需要,虽然它会持续地重塑(reshape)“编程”的含义。

 

第7部分 软件工艺(Software Craftsmanship)

第31章 布局与风格(Layout and Style)

要点

  • 可视化布局的首要任务是指明代码的逻辑组织。评估该任务是否实现的指标包括准确性、一致性、易读性和易维护性。
  • 外表悦目比起其他指标是最不重要的。然而,如果其他指标都达到了,代码又质量好,那么布局效果看上去也会不错。
  • Visual Basic具有纯代码块风格,而Java的传统做法就是使用纯块风格,所以若用这些语言编程,就请使用纯代码块风格。C++中,模拟纯代码块或者begin-end块边界都行之有效。
  • 结构化代码有其自身目的。始终如一地沿用某个习惯而少来创新。不能持久的布局规范只会损害可读性。
  • 布局的很多方面涉及信仰问题。应试着将客观需要和主观偏好区分开来。定出明确的指标,在此基础上再讨论风格参数的选择。

第32章 自说明代码(Seif-Documenting)

编码时要把维护你程序的人想象成知道你住址的有严重暴力倾向的精神病人。——佚名

易读性的最高水平:自说明代码。这代码通过好的编程风格承担文档说明的很大一部分任务。对于精心编写的代码而言,注释不过是美丽衣裳的小饰物而已。

要点

  • 该不该注释是个需要认真对待的问题。差劲的注释只会浪费时间,帮倒忙;好的注释才有价值。
  • 源代码应当含有程序大部分的关键信息。只要程序依然在用,源代码比其他资料都能保持更新,故而将重要的信息融入代码是很有用处的。
  • 好代码本身就是最好的说明。如果代码太糟,需要大量注释,应先试着改进代码,直至无须过多注释为止。
  • 注释应说出代码无法说出的东西——例如概述或用意等信息。
  • 有的注释风格需要许多重复性劳动,应舍弃之,改用易于维护的注释风格。

第33章 个人性格(Personal Character)

编程过程非常耗用脑力,这种特性使得个人性格显得很重要。人们都知道聚精会神地一天工作八小时有多么困难!编程工作本质上是项无法监督的工作,因为没人真正清楚你正在做什么。我们都经常有这样的经历——耗用80%的时间着力与项目的个别地方,而花费20%的时间来完成其余80%的工作。

如果你工作10年,你会得到10年经验还是1年经验的10次重复?必须检讨自己的行为,才能获得真正的经验。只有坚持不懈地学习,才能获取经验;如果不这样做,就无法得到经验,无论你工作多少年。

要点

  • 人的个性对其编程能力有直接影响。
  • 最有关系的性格为:谦虚、求知欲、诚实、创造性和纪律,以及高明的懒惰。
  • 程序员高手的性格与天分无关,而任何事都与个人发展相关。
  • 出乎意料的是,小聪明、经验、坚持和疯狂既有助也有害。
  • 很多程序员不愿主动吸收新知识和技术,只依靠工作时偶尔接触新的信息。如果你能抽出少量时间阅读和学习编程知识,要不了多久就能鹤立鸡群。
  • 好性格与培养正确的习惯关系甚大。要成为杰出的程序员,先要养成良好习惯,其他自然水到渠成。

第34章 软件工艺的话题(Themes in Software Craftsmanship)

要点

  • 编程的主要目的之一是管理复杂性。
  • 编程过程对最终产品有深远影响。
  • 合作开发要求团队成员之间进行广泛沟通,甚于同计算机的交互;而单人开发则是自我交流,其次才是与计算机。
  • 编程规范一旦滥用,只会雪上加霜;使用得当则能为开发环境带来良好机制,有助于管理复杂性和相互沟通。
  • 编程应基于问题域而非解决方案,这样便于复杂性管理。
  • 注意警告信息,将其作为编程的疑点,因为编程几乎是纯粹的智力活动。
  • 开始时迭代次数越多,产品的质量越好。
  • 墨守成规的方法有悖于高质量的软件开发。请将编程工具箱中填满各种编程工具,不断提高自己挑选合适工具的能力。
comments powered by Disqus