




已阅读5页,还剩40页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第一章 善于防守使程序正确和使程序能用的区别:1. 编写在大多数情况下都能用的代码是很容易的,它对常规的输入集会产生常规的输出集;2. 正确的代码绝对不会崩溃,对于所有的输入集,它的输出都将是正确的;3. 并非所有正确的代码都是优秀的代码,因为有些正确的代码的逻辑可能很难理解,难以维护。在编写代码的时候,你会很容易产生很多设想,例如程序应该如何运行、如何调用、如何输入等。我们经常有以下3种常见的设想:1. 这个函数“绝对不会”被那样调用,传递给我的参数总是有效的;2. 这段代码肯定会“一直”正常运行,它绝对不会产生错误;3. 如果我把这个变量标记为“仅限内部使用”,就没有人尝试访问这个变量。不要做任何设想,没有记录下来的设想会不断的制造缺陷,特别是随着代码的增长。凡是可能出错的事,准会出错。防御性编程是一种细致、拘谨的编程方法。为了开发可靠的软件,我们要设计系统的每个组件,以使其尽可能的“保护”自己,我们通过明确的在代码中对设想进行检查,击碎了未记录下来的设想。这是一种努力,防止我们的代码被以一种错误的方式调用。防御性编程不是检查错误,不是测试,也不是调试。对于防御性编程的反对观点:1. 降低了代码开发的效率;2. 混淆了代码的职责,代码在执行必要的业务逻辑外,也要对各种设想进行判断。对于防御性编程的支持观点:1. 节省大量的调试时间;2. 编写可以正确运行、只是速度有些慢的代码,要好于编写那些大多数时间都正常运行、但是有时会崩溃的代码;3. 我们这里所考虑的大部分防御性措施,并不具有任何明显的性能开销;4. 防御性编程避免了大量的安全问题。我们可以使用一些编程技巧来进行防御性编程使用好的编码风格和合理的设计。这样可以防范大多数的编码错误。最好地计算机程序的文本是结构清晰的。不要仓促的编写代码。要三思而后行,一方面,要对将要写的代码出现的各种可能情况进行分析;另一方面,一定要在完成与一个代码段相关的所有任务后,再进入下一个环节。不要相信任何人。不要设想所有的一切都运行良好,或者所有的代码都能正确的运行,用怀疑的眼光审视所有的输入和输出,知道你能证明它们是正确的。编码的目标是清晰,而不是简洁。请保持代码简单。不能维护或者难以维护的代码是不安全的。不要让任何人做他们不应该做的修补工作。内部的事情就应该留在内部。在开始编码时就应用防御性策略,要比改进代码时才使用容易的多。编译时打开所有警告开关。编译器的警告可以捕捉到很多愚蠢的编码错误。在任何情况下都应该打开你的编辑器的警告功能,警告的出现总是有原因的。使用静态分析工具。使用安全的数据结构。检查所有的返回值。不要让错误悄无声息的侵入你的程序;忍受错误会导致不可预知的行为。不论何时,都要在适当的级别上捕获和处理相应的一场。审慎的处理内存和其他宝贵的资源。对于在执行期间获取的任何资源,必须彻底释放。不要因为觉得操作系统会在你的程序退出时清除程序,就不注意关闭文件或者释放内存。在声明位置初始化所有变量。即应该将变量的声明和初始化放在一起,这样可以放置对未初始化的变量进行操作。尽可能推迟一些声明变量。可以使变量的声明位置和使用它的位置尽量接近,从而防止它干扰代码的其他部分。不要在多个地方重用一个临时变量,变量重用会使以后对代码重新完善的工作变得一场复杂。使用标准语言工具。使用好的诊断信息日志工具。审慎的进行强制转换。强制转换不仅可能会造成转换一场,还可能造成变量的精度发生变化。我们可以使用约束来对我们的设想进行验证。约束分为:1. 前置条件; 2. 后置条件;3. 不变条件;4. 断言。优秀的程序员:1. 关心他们的代码是否健壮;2. 确保每个设想都显式的体现了防御性代码中;3. 希望代码对无用信息的输入有正确的行为;4. 在编程的时候认真思考他们所编写的代码;5. 编写可以保护自己不受其他人的愚蠢伤害的代码。糟糕的程序员:1. 不愿意去考虑他们的代码出错的情况;2。 为集成才发布可能出错的代码,并希望别人会找到错误;3. 将关于如何使用他们代码的信息仅仅攥在手里,并随时都可能将其丢弃;4. 很少思考他们正在编写的代码,从而产色很难干不可预知和不可靠的代码。第二章 精心布局编码的风格曾经是、现在是、将来也将继续是程序员之间信仰之争的主题之一。编程样式极大的影响了代码的可读性,没有人愿意使用读不懂的代码。我们的代码一般有3类读者:1. 我们自己,我们不仅需要能够读懂刚刚写下的代码,也需要在多年之后仍然能够明白它的意思;2. 编译器,关注语法,不关注样式;3. 其他人,这类人最重要,也最容易被忽视。什么是好的样式?1. 一致;2. 传统,采取一种业内流行的样式;3. 简洁。括号的样式风格:1. K&R风格;2. 悬挂式风格;3. 缩进式风格;4. 其他风格。好的样式风格不止一种,各种风格的品质和适用性取决于使用的环境和文化背景。选择一种好的编码风格,并坚持使用它。多种风格有助于我们写出更好的代码。对于一个组织来讲,是需要一种内部的编码风格的。这样可以提高代码的质量,并使软件开发更加安全。所有人共同分享同一种风格所带来的好处,远远超出了为了同意而付出的代价,即使不赞同这个标准,你也应该遵守它。当一个组织内么有一个明确的编码风格时,我们需要制定一份。这是一项需要谨慎考虑的任务。应该恰当而坚决的开展。一个良好的个人风格对于整个团队的程序员不一定是最好的。你要创建的标准不应当只适合你自己的审美习惯,它应该使团队的代码合为一体,并避免常见的问题。创建编码风格时可以采取的策略:标准为谁而立?圈定使用范围。赢得广泛参与。有参与才会更乐意遵守。做出成果。最终应该有一份可以理解的文档。使最佳做法标准化。突出重点。避免热点。把罕见且麻烦的情况留给个人决定,前提是不会造成太大的分歧。逐步完善。为推广做计划。创建编码风格时,需要注意掌握分寸,不是规定的越细越好。我们有理由认为:版面漂亮的代码是经过精心设计的代码,格式混乱的代码一定没有好好下功夫。优秀的程序员:1. 避免无意义的争论,对别人的意见非常敏感;2. 谦虚克己,不认为自己永远是对的;3. 懂得版面是如何影响可读性的,并努力写出可能清晰的代码;4. 采用内部样式,即使内部样式与他们的个人偏好相矛盾。糟糕的程序员:1. 闭目塞听,固执己见,认为”我的观点才是正确的“;2. 和每个人争论最琐屑的小事,把这些当成成证明自己优势的机会; 3. 没有始终如一的个人编码风格;4. 对别人代码的风格指手画脚。第三章 名正言顺远古的人认为命名某个事物就是对其拥有权利。这不仅仅是简单的宣称所有权。一些人对名字的力量坚信不疑,以至于他们从不将自己的名字告诉陌生人,因为他们害怕陌生人会使用名字来伤害他。名字所描述的内容包括:1. 身份;2. 行为;3. 识别,名字将一个事物标明为一个性质独特的实体。我们为什么应当恰当的命名呢?因为编写源代码就是关于如何进行清晰的交流,恰当的命名意味着“了解名称就可以了解对象”。清晰的命名是优秀代码的特点之一,一个对象的名称应该清晰的描述了这个对象。在编码时,需要进行命名的内容包括:1. 变量;2. 函数;3. 类型; 4. 命名空间或者包的名字;5. 宏; 6. 源文件名称。为了恰当的命名,在为一个对象想出名称以前,必须准确的了解这个对象是什么,如果你不知道你所命名的对象是什么,它的用途和它存在的理由,你怎么能够赋予它一个有意义的名字呢?所以说糟糕的名称通常表明你对需要命名的对象缺乏理解。一个好的名称具有以下特点:技术上正确。符合所使用的语言在命名上的限制。符合语言习惯。要了解你的语言的命名规则,更重要的是,了解这种语言的习惯用法,公共命名约定是什么?应用这些语言习惯和约定。恰当。包括以下几点:1. 长度,在进行命名是,将重点放在清晰而非简洁上,慎用各种奇怪的所写;2. 格调,一般在命名时不要使用语气助词。理解在较短的名称和较长的名称之间进行权衡的考虑因素,即它们是如何依赖于变量的作用范围的,对于循环计数器这种变量,就可以简单的以i来命名,但是要注意在多重循环时,最好起一个易理解的名字。如何命名一个变量:一般情况,变量用一个名词来表示。如何不是名词,通常会是一个名词化的动词。有很多用于修饰成员变量的面向对象的语言约定,以说明它们是成员,而不是局部变量或者全局变量。一种常见的命名习惯是使用首字母缩略词作为简洁和“有意义”的名称,例如:我们可以这样声明一个变量:SomeTypeWithMeaningfulNaming stwmn(10);一般情况下,将类姓名和变量名区分开,类姓名通常都有一个大写的首字母,而变量名则具有小写的首字母,例如:我们可以这样声明一个变量:Window window;如何命名函数:如果变量像一件可以握在手里的东西,那么函数就是你拿这件东西所做的事,它的名字在逻辑上更应该是一个动词。有意义的函数名字应该避免使用be、do和perform等词语。我们应该隐藏函数内部的具体实现过程,始终从使用者的角度来为函数命名。但是当在为请求信息的简单查询函数进行命名的时候,可以不必遵守。当你编写一个函数时,应该使这个函数很好的文档化,其名称应该仍然是关于函数用途的一个清晰的描述,这是函数契约的一部分。如何命名类型:对类的命名没有太多严格的直觉推理法,因为不同的类有不同的用途。在名称中避免使用多余的词,尤其实在类型名中避免使用以下这些词语:class、data、object和type。要确保你描述的数据的类不是一个真正的对象。糟糕的类名会使程序员们感到困惑,较好的命名能为代码的逻辑提供清晰的思路。如何命名名字空间:选择描述其内容的关系的词语。不要选择重复暗示命名对象是一个集合的名称,例如:controls_group就不是一个好的名称。如何命名宏:名字全部使用大写字母。不要为其他任何对象使用这种全部大写的名称。如何命名文件:注意大小写,一些文件系统不区分大小写。确保同一目录中的文件名不仅仅只是大小写不同。尽量保证你创建的文件都具有不同的名称,即使这些文件都分布在不同的目录中。一个好的名字,具有以下特点:保持前后一致,命名一致不仅限于创建名称时的大小写和格式化,一个名称将确立一个隐喻,在这个程序中,这写隐喻应该保持一致。利用代码的内容,我们要充分利用上下文,写出简洁的、描述性的以及没有多余信息的名称。使用对你有利的名字。一个好的名字,不应该具有的特点:含义模糊。啰嗦。不准确或使人误解。有歧义或者含糊不清。太做作。优秀的程序员:1. 意识到名字的重要性并尊重它们;2. 为他们创建的所有对象都考虑如何进行命名,并且选择恰当的名称;3. 使许多考虑因素保持平衡,包括名字的长度、清晰度、上下文等;4. 从整体的视角来命名,因此他们的名称在一个项目中都保持一致。糟糕的程序员:1. 不关心他们的代码是否清晰;2. 很快的编写代码而很少进行思考;3. 忽视语言的自然习惯;4. 命名时前后不一致;5. 不从整体的角度进行思考,不考虑他们的代码怎样和整体相结合。在许多不同的上下文中,重复使用同一个局部变量名是完全可以接受的。不要用局部变量名隐藏全局变量名,这样容易造成混淆。优秀的编码高手会把良好的命名当做一种习惯,而且不需要编码规范来“约束”。第四章 不言自明创建优秀的代码以为着创建良好文档化了的代码。编写代码是一件艰苦的工作,将代码文档化更是艰苦异常。就像代码一样,文档必须接受版本控制,你必须要确保你阅读的文档的版本和你正在处理的代码的版本保持一致。唯一能够完整并正确的描述代码的文档就是代码本身。这并不自然而然的以为着代码本身就是最佳的描述,但是在通常的情况下,这是你能够获得的唯一文档。编程语言是我们交流的媒介,清晰地交流至关重要。清晰的代码会活的较高的质量,因为你犯错误的可能性降低了,而且这样的代码维护的成本也比较低。自文档化的代码有很高的可读性。如何编写自文档化的代码:使用好的样式编写简单的代码。1. 让“正常”的流程明显的贯穿你的代码,错误的情况不应该扰乱程序的正常执行流程;2. 避免过多的使用嵌套语句;3. 要谨慎的优化代码,如果一定要优化,那么要清晰的注册这段代码发生了哪些变化。选择有意义的名字。好的名字也许是我们避免多余注释的最好办法。分解为原子函数。1. 一个函数,一种操作;2. 减少任何出人意料的副作用;3. 保持简短,短小的函数易于理解。选择描述性的类型。命名常量。避免在程序中进行“硬编码”,避免使用神奇数字。强调重要的代码。1.在类中按一定顺序进行声明;2. 尽可能隐藏所有不重要的信息;3. 不要隐藏重要的代码;4. 限制嵌套的条件语句的数量。要确保所有重要的代码非常突出,并且易于阅读,将任何目标读者不关心的内容都隐藏起来。分组相关信息。提供文件头。在文件的顶部放置一个注释块,以描述文件的内容以及该文件所属的项目。恰当的处理错误。在最恰当的上下文中处理错误,不要返回无意义的错误。编写有意义的注释。清晰的代码应该包含“适量”的注释(“适量”:只有在你无法以任何其他方式来提高代码清晰度的情况下,再添加注释)。实现自文档化的方法:文学性编程。你不是在编写程序,你是在编写文档。源代码就是文档。高质量的文档永远都取决于编写策划那个徐的程序员。文学性编程实际上是带有设计的传统代码与实现规范这两者的结合。文档化工具。例如JavaDoc和NDoc。写作的技巧就是创建一个上下文环境,别人在其中思考。优秀的程序员:1.努力编写清晰、自文档化的代码;2. 尽量编写所需的最小量的文档;3. 考虑哪些需要维护其代码的程序远门的需求。糟糕的程序员:1. 对于他们所编写的难以理解的代码非常骄傲;2. 尽力避免编写各种文档;3. 不关心更新文档的问题;4. 认为:如果我写起来很困难,那么别人理解起来也会很困难。如何将相关代码进行分组,一般有以下几种方法:1. 名称前缀或后缀;2. 文件系统中的位置;3. 将相关内容放置到一个类中或者结构中。如果有良好的代码文档,那么一个新手就应该很清楚代码中的某个部分有什么作用。编码规范或者编码风格虽然有一定作用,但是没有什么可以取代精明、合理的编程。愚蠢的程序员即使遵守最严格的标准,也仍然会写出很差的代码。 第五章 随篇注释注释非常像意见,你可以随心所欲的做注释,但这仅仅是因为做注释并不意味着这些注释就是正确的。注释可以将优秀的代码和糟糕的代码区分开,将粗糙复杂艰涩难懂的逻辑与清晰友好的算法区分开。但是我们不需要过分夸大注释的作用,如果你已经编写出真正优秀的代码,那么注释就像蛋糕上的糖衣。好的注释是避免让人望而生畏代码的一种策略,注释本身并不能够让糟糕的代码变得好一些。什么是代码注释?从语法的角度来看,注释就是编译器将忽略不计的原代码块;从语义的角度来看,注释是昏暗肮脏的小路和明亮通畅的大道之间的区别,你可以使用它来强调某个特殊的问题领域,或哟工作头文件的记录媒介。注释的目标读者是人,而不是计算机,如果我们想要提高注释的质量,就必须了解并满足人们在阅读代码时所真正需要的。代码注释不是你应该放在代码中的唯一文档。注释不是规范,不是设计文档,也不是API参考。注释是一种总是会物理的附在代码上的宝贵的文档形式,是一种内部的文档化机制。注释的形式各种各样,从根本上说,编写注释是一个主观的问题。学会只编写够用的注释,过犹不及,我们要重视质量,而不是数量。阅读你的注释的人也会阅读你的代码,因此尽可能在代码本身中进行文档化,而不要编写大量的注释。要将你的代码语句看做是第一级的注释,并让代码自文档化。编的好的代码实际上并不需要注释,因为每行代码都可以自我解释。我们应该把时间花在编写不需要大量注释支持的代码上。注释中应该有哪些东西:解释为什么,而不是怎么样。你应该改为几种描述为什么有些东西要这样写,或者下一个语句块最终要起到什么作用。当我们维护一段代码时,解释这段代码为什么存在的理由很少发生变化,但是这段代码的实现其目的的方法却很容易发生变化。不要描述代码。无价值的描述性注释有时很明显,没有必要用英语费力的重复叙述代码,除非你要文档化一个相当复杂的没有注释就无法理解的算法。不要取代代码。当你发现自己在编写密密麻麻的注释来解释你的代码时,赶快停下来,想一想是不是有一个更大的问题需要解决。确保注释有用。好的注释犹如好的代码;它有如下特点:1. 记录意想不到的内容;2. 讲真话;3. 有价值;4. 清晰明了;3. 容易理解。避免分心。注释的作用是说明周围的代码,因此我们必须避免所有干扰这个作用的因素。注释应当只是增加价值。需要避免以下形式的注释:1. 过去的事情;2. 你不想要的代码,不要把需要剔除的代码包含在注释中;3. ASCII技术;4. 代码块的结尾,应该与开头一起先是在同一页中,而且代码的版面应该是块的开头和结尾都一目了然。不要文档化差劲的代码重写这些代码。良好的注释应该有些原则是可以参考的,如下:一致性。所有的注释都应该清晰明了,前后一致。为你的注释选择一种特定的布局方式,并且始终坚持使用这种方式。清晰的块注释。要充分考虑到你的注释可能会被看到的环境,不要太沉迷于具有高亮显示的编辑器中。清晰的注释应该让文字上下对齐,使得在打印后也可以易于阅读。缩进的注释。注释不应该截断代码,或者打乱逻辑流程。让注释的缩进位置和周围的代码保持一致。行尾注释。在注视和代码之间保留空白是一种好习惯。帮助你阅读代码。通常注释在它描述的代码的上方,而不是下方。选择一种维护成本较低的风格。在美观的源代码和维护成本之间,总是存在一种平衡,与得到丑陋的代码相比,我更倾向于在维护上花一些时间。分隔板。如果你的函数太大,以至于你需要一些视觉上的线索来提示函数的开头和结尾,那么你应当修改你的代码。标志。注释还可以用作代码中内嵌的标志。文件头注释。所有的源文件都应该一描述其内容的注释块作为开头,其中应该包含的信息是文件的目的和描述所有权及版权信息的版权声明。头注释中不应该包含容易过时的信息,也不需要包含描述每次都做了哪些修改的源文件历史记录。编写注释的两种方式:首先用注释构造代码的结构,然后在各行注释下面填写代码。首先徒手编写新的代码,然后添加必要的注释。有经验的程序员会编写代码边写注释。实践会告诉你使用多少注释是正确的。在错误得到修正的地方放置一个通告是一种常见的但是存在争议的注释习惯。在作出不明显的修改时是否应该添加注释,以防止别人在日后修改代码时又引入新的错误,对这个问题仍然存在着争论。注释会过时的,所有粗心维护的代码都会过时,从而招致很多难看的缺陷,并且失去原本整洁的设计。注释过时的速度似乎比任何其他代码过时的速度都要快,他们会随着他们所描述的代码一起过时。当你修正、添加或者修改任何代码时,修正、添加或修改周围的相关注释。我们必须让注释足够简单以便于更新,否则它们很容易过时。如果你发现一段注释确实不正确或有误导作用,那么你应当把修改这段注释作为你的代码维护工作的一部分。注释不会比它们所标注的代码更重要你无法使用注释把糟糕的代码变好,你的目标应该是编写根本无需注释的自文档化的代码。优秀的程序员:1. 尽量只写少量的好注释;2. 编写解释为什么的注释;3. 集中精力编写优秀的代码,而不是过多的注释;4. 编写有意义。有帮助的注释。糟糕的程序员:1. 不能说出好的注释和差的注释之间的差异;2. 编写解释如何做的注释;3. 并不介意注释只是对他们自己有意义;4. 使用许多注释来支持糟糕的代码;5. 用多余的信息来填充源文件。第六章 人非圣贤错误可能而且必将发生。几乎任何操作都会带来意想不到的结果,这种结果与有缺陷的程序中的bug不一样,因为你预先就知道错误会发生。如果你不编写代码来处理这些错误情况,那么你几乎肯定最终会遇到bug;你的程序不会总按你的意愿执行。错误产生得原因可以分为以下三种:用户错误。一个好的程序会指出错误所在,并帮助用户来改正它。程序员错误。由程序员引入的代码缺陷。意外情况。我们需要一个定义良好的策略来应对我们代码中的各种错误。不论是由人来选择怎么处理问题,还是由代码来决定一定要由某人或者代码来负责确认错误和对错误作出反应。错误由下层组件提出并向上传播,以便让调用方来处理。错误以什么方式提出,称为错误报告机制,可以分为以下几种:不报告。这是最简单的情况。绝对不要忽视任何一种情况,如果你不知道如何处理这个问题,就向调用代码发送一个故障信号,不要对错误心存侥幸。返回值。从函数返回一个表示成功或者失败的值,一个更高级的方法是列举出所有可能的退出状态,并返回一个相应的原因代码,一个值表示成功,其余的值分别代表许多不同的异常终止信息。错误状态变量。这种方法试图解决函数的返回值和它的错误状态报告之间的矛盾。函数将设置一个共享的全局错误变量,而不是返回一个原因代码。但是这种方法在多线程时会很麻烦。从功能上讲,这种技术与使用返回值是等价的,但是它的缺陷之多足够让你放弃使用。异常。异常是一种管理错误的语言工具;不是所有的语言都支持异常。异常是不能被忽略的,如果没有被捕获和处理,异常就会传播到调用堆栈的最高层,这通常会导致程序崩溃,这使得异常相对于手工编写的错误处理代码来说更加整洁和安全。处理一个异常的代码与引发该异常的代码截然不同,而且可以差的很远;异常并非没有开销,语言对异常的支持会造成性能上的损失。信号。它是一种更加极端的报告机制,主要用于由执行环境向运行程序报告错误,信号是硬件中断的软件等价物。有恢复能力的代码一定是针对异常安全的,不论出现什么异常,它都必须正确的运行。异常中立的代码将所有的异常都传播到调用方,它不会隐藏或者改变任何东西。异常安全分为以下3个等级:1. 基本保证,如果异常在一个函数中发生,它将不会泄露资源,代码的状态将保持连贯,但是它不一定会保持一种已知的状态;2. 强力保证,如果通过你的代码传播了一个异常,程序的状态将保持完全不变;3. 不抛出保证,操作永远不能抛出异常,如果我们是异常中立的,那么就意味着函数不能执行任何可能会抛出异常的操作。根据不同的错误报告机制,我们有不同的检测错误的方式:返回值。错误状态变量。异常。只有在知道可能会抛出异常的情况下,你才能作出明智的选择,而只有这已被文档化的情况下,你才能够知晓。信号。需要为其安装一个处理程序。在对一个错误进行处理前,我们需要了解错误相关的一些关键信息:错误来自何处。当时的上下文环境。为什么会出错。错误是何时发生的。错误的严重性。如何修正错误。我们在何时处理错误,可以分成两派:1. 尽可能早的处理,因为错误是在其来源附近处理的,所以你可以留住重要的上下文信息,从而使得你的代码更加清晰,对于返回错误代码的函数来说通常是最佳的选择;2. 尽可能推迟处理,因为检测到错误的代码很少知道应该怎样进行处理,如何处理通常取决于错误出现的环境,即调用环境,异常对于这种处理方式很适合。我们在修正错误时可能会用到的技巧:1. 日志,日志的作用是记录程序的生命周期中值得注意的事件,以使你可以深入研究程序内部工作方式和执行的重构路径;2. 报告,程序应该只在已没有什么可作的情况下才向用户报告错误;3. 传播,如果你的代码遇到了一个错误并且不知道该怎么办,那么就把错误向上传,很可能你的调用方有能力解决这个问题;4. 忽略,如果采取这种方法,必须在代码中明显的标记出来。如果你要提示用户来解决问题,那么首先应该在头脑中有一些总体的思路:用户并不像程序员那么思考,所以以他们所期望的方式来呈现信息。确保你的信息不显得高深莫测。不要呈现毫无意义的错误代码。将后果严重的错误与仅仅是警告区分开。只在用户完全理解每种选择的后果时在提出问题,如果需要的话,对问题进行一些解释,使每种选择的后果都非常清晰。每个函数中都应该执行的一些错误检查:检查所有的函数参数。检查执行过程中关键点处的不变条件是否满足。在使用任何来自外部的值之前都要检查其有效性。检查所有系统调用和其他下级函数调用的返回状态。在编写可能会失败的代码的同时,编写所有的错误检测和处理,不要拖延到以后做,如果你很不幸必须推迟处理,至少现在就编写错误检测部分。既考虑错误的引发又考虑错误的处理的一般原则是无论故障在什么情况下出现,对于故障的处理都有一个一致的策略。你所编写的每行代码都必须在恰当和彻底的错误检查与处理上取得平衡。我们应该如何处理在错误处理代码中发生的错误?应该像对待其他任何错误一样,处理那些在错误处理程序中发生的错误,你会得到一个多层嵌套的错误处理程序。优秀的程序员:1. 将好的意图与好的编码习惯结合在一起;2. 在编写主要代码的同时编写错误处理代码;3. 在他们编写的代码中全面覆盖了所有的错误可能性。糟糕的程序员:1. 编写代码时很随便,既不思考也不检查他们在做什么;2. 忽略在编写代码时出现的错误;3. 最终不得不进行漫长的调试过程以找到程序崩溃的原因,因为他们从不预先考虑错误情况。第七章 欲善其事,先利其器要创造出非凡的代码,不仅需要有技艺精湛的编程高手,还要有好用的工具和灵活运用工具的能力。你使用工具的方式可以看出你是否能称为一名真正多产的程序员,在极端的情况下,这些工具可以提供决定你的项目成功与否的简化操作,软件工厂哪不懈的前进步伐,要求你紧紧抓住任何可以帮助你编写更好的代码,以及更快和更可靠的编写代码的工具。我们用于构建软件的工具多种多样,一般在以下几方面会有所不同:复杂性,有些工具是精心设计的环境,具有许多功能和不可思议的可配置性,还有一些则是为了但个人物而设计的小巧的工具。使用频率,有些工具使用的非常频繁,离了它们我们就没法生活,而有一些工具我们好久才使用一次,但是当你需要它们时就会发现它们价值连城。接口,有的工具有着非常美观的GUI,而有些则只是CLI接口。集成,有些工具适合较大的工具链,常常包含在一个图形接口的集成开发环境中,独立的命令行工具主要用作一种数据过滤器,倾向于生成纯文本的输出,其输出的格式适合作为其他工具的输入。成本,许多免费工具都很优秀,但是一般来说付出多少就得到多少,免费工具的文档一般较差,支持较少。质量。尽可能全面的了解你的常用工具,为了熟悉这些工具而投入的少许时间会让你很快就有所收获。工具不是替我们做我们该做的工作,而是使我们有能力做我们的工作,软件的质量总是取决于程序员的能力。成为一名成熟的程序员的条件之一,就是明白不同的情况下需要不同的解决方案,并将正确的工具应用到正确的工作上。用实际的眼光来对待软件工具仅当工具能够让你的生活轻松些的时候再使用它们。我们需要了解可用工具的种类,确保自己知道从哪里可以活的这些工具,即使你现在还不需要它们。了解它能做些什么。查清它的功能有哪些,即它究竟能做些什么,而不是你觉得它应该能做什么。学习如何驾驭它。仅仅因为你在运行某个工具时没有出错,并不能说明它是完全按照你的要求来完成工作的,你必须懂得如何正确使用它,并却性你可以让它执行你的命令。找出使用每种工具的最好方式。了解它适合什么任务。了解每个工具是如何融入到其他可用工具的环境中的,将合适的工具用到合适的任务中,不要拿着牛刀去杀鸡。检查它是否可用。每个人都有可能在某个时刻称为糟糕工具的牺牲品,对于诊断你所遇到的任何问题来说,拥有你的工具的源代码是非常有用的。找到了解更多信息的途径。你不必事事都了然于心,关键是要认识那些明白的人。查明新版本何时出现。工具的数目多的惊人,而且增长的速度很快。你的工具套件到底包含哪些工具将取决于你的工作类型。源代码编辑工具包括:源代码编辑器。可能是你最重要的工具,比编译器还要重要。你应该选择一个你用起来感觉很舒服并且能够完成你的任务的编辑器。源代码处理工具。源代码浏览工具。版本控制。源代码生成工具。源代码美化工具。代码构建工具包括:编译器。链接器。对于JAVA和c#来说,链接器是与运行时环境结合在一起的。构建环境、测试工具链。测试工具是代码的构建工具,而不是调试工具。调试和调查工具包括:调试器。拥有一个高质量的调试器并了解如何使用它,可以为你节省大量用于追查意外行为的开发时间。所有IDE和工具链都有其自己的调试器。分析器。分析器用于分析代码各部分的运行时间并找出瓶颈所在。代码校验器。代码校验器分为静态和动态两种,前者以一种与编译器类似的方式整理代码,检查你的源文件,以确定可能存在问题的区域以及对语言的错误使用,例如lint;后者在代码被编译时对代码进行修改和茶装,然后在运行时执行检查,例如内存分配/边界检查器。度量工具。它们会生成关于代码质量的统计评估,有效地指出最脆弱的代码区域,通常都是以函数为基础来进行收集的,最基本的度量数据就是代码的行数,其次就是注释与代码的比率。反编译程序。缺陷跟踪。提供一个共享的数据库,其中包含了你的系统中找到的bug的跟踪记录。语言支持工具包括:编程语言。语言本身也是一种工具。掌握几种语言,每个语言都会教给你一种解决问题的不同方式,将语言作为工具来考虑,为每项任务选择最合适的语言。运行时和解释程序。大多数语言没有必需的运行时支持就无法使用。组件和库。其他工具包括:文档工具。良好的文档非常宝贵,它是设计良好的代码的关键部分。文档不仅需要编写,也要被阅读,好的在线帮助系统非常重要。项目管理。是你可以报告和跟踪工作的进度、管理缺陷和监视团队的绩效等。当我们无法为一项任务找到合适的工具,而手工完成它看起来有时不可能时,我们可以使用“自行设计”的工具,如果这项任务会重复的出现,短暂的工具开发从长期来看可以为你节约大量的时间。创建工具的方式包括:以一种新的方式来组合现有的工具。使用脚本语言。从零开始创建完善的程序。在编写工具时,需要考虑以下因素:使用者的期待。是否可以扩展一个现有工具。任何程序员都应该拥有的最小工具集包括:1. 某种形式的编辑器;2. 必需的最小语言支持;3. 运行这些工具的计算机。一个高效的程序员的工具箱一般还至少包括:1. 代码版本控制系统;2. 一套比较好的库;3. 一个构建工具。对你的工具箱抱有一种职业的、负责的态度,将使你成为一名更好的程序员。优秀的程序员:1. 愿意一次学会如何使用一种恰当的工具,而不愿意不断的重复执行一项单调乏味的工作;2. 了解不同的工具链模型,并且使用起来每个都感觉很舒服;3. 使用工具以使他们的生活更简单,但是不会成为工具的奴隶;4. 将他们所使用的所有东西当做是工具,一种可以替换的工具;5. 很有效率,因为他们对工具的使用是第二天性。糟糕的程序员:1. 了解某些工具的使用方法,并且从这些工具的角度来看待所有不同;2. 害怕花时间来学习新的工具;3. 从一开始使用某种开发环境,到现在一直近乎虔诚的使用它,从不尝试改变,甚至不尝试了解其他开发环境;3. 当遇到一个颇有价值的新工具时,并不将其添加到他们的工具箱中。第八章 测试时代不论你觉得自己是一位多么优秀的程序员,发布未经检测的软件都无异于自杀。有太多的软件工厂不是低估了详尽测试的重要性,就是试图把测试挤到发布软件之前的最后一分钟来匆忙的完成。测试是一种重要的代码构建技术。在软件开发中,以下几个术语是经常被混淆的:“错误”:指某件你做错的事,它是一种特定的人类行为,会造成软件包含缺陷。“缺陷”:指错误在软件中体现出来的后果。缺陷的隐蔽性使调试变得异乎寻常的困难。“故障”:当缺陷被引发时,它可能会造成一个故障,但也可能不会。故障是缺陷的表现形式,也是我们真正担心的问题。它也许是我们唯一能注意到的事件。“bug”:通常用作缺陷的同义词。良好的测试是一种技能,测试最重要的一条原则:就是要去做。测试是一种与调试截然不同并且相互独立的活动,尽管他们之间的界限很模糊,并且经常混淆在一起,测试是一种检验你的软件是否存在缺陷的系统过程,而调试则是一种跟踪造成缺陷的原因的过程。在软件的整个开发过程中,需要对各种不同的对象进行测试:大量的文档会经历一个测试阶段,通常也称为”审查“。实现规范的代码自然将在开发人员的计算机上进行测试。对最终产品进行测试,主要检查系统的功能是否和期望一致。测试和QA也是不一样的:测试的目标是找出错误的行为,即软件与它的规范不符的地方;QA是预防,它确保了我们的开发过程会生成高质量的软件。开发过程中每个人都会对制作高质量的软件有所影响,软件的质量并不是你可以在代码完成之后添加的品质。为什么要进行软件测试?1. 帮助我们找到缺陷并修正它们;2. 确保以后的版本中不会出现同样的缺陷。测试不能证明不存在缺陷,而只能证明存在缺陷。不要因为代码通过一组不完全的测试,就产生假的安全感。谁来进行测试?程序员有责任对他编写的源代码进行测试。QA部门的工作是进行测试,但是应该测试产品,而不是测试新编写的代码。测试的内容有哪些?我们主要测试策略是演练所有代码,并通过编写更多的代码来验证它们的行为;另一种策略是直接检查代码,以验证它的正确性。何时进行测试?在编写代码的同时进行测试,这是找出代码错误的最早的机会,尽早的和详尽的进行测试,是确保软件质量最有效的方式。bug的成本是会随着开发过程的进行而日益增加,所以尽可能早一些开始测试代码非常重要。测试驱动开发主张将测试作为一种核心的构造手段,即你在编写被测试的代码之前就开始编写测试它的代码。这种策略有深刻的寓意:当你开始考虑编写某段代码时,你必须同时考虑如何对它进行测试,这将使你设计代码的方式更臻完善。持续集成的策略:我们执行测试越频繁,就越有可能发现问题。为了测试一段特定的代码是否可以正常运行,你需要一个测试用例来验证以下两点:对于所有有效的输入,都会产生正确的输出。对于所有无效的输入,都会产生适当的故障行为。一般人都很容易相信自己所读到的代码,并相信它是正确的,在你刚写下一些代码时,只会读到你想写的内容,而不是你真正写下的东西,要学着看两遍,用批评的眼光来阅读所有的代码。导致测试比较难以进行的因素包括:代码规模。依赖关系。外部输入。外部激励。代码可以对除函数调用之外的激励作出反应。如果这些激励可能出现的时间和次数都不确定,那么就会特别麻烦。线程。多个控制线程会使测试更加复杂。演变。硬件缺陷。难缠的故障形式。代码可能会以多种令人惊讶和奇怪的方式崩溃。编写测试用例绝非易事,当组件组合在一起并开始相互依赖时,软件的复杂度将城指数级增长,所有这些问题凑在一起,会让你的生活变得复杂无比。无论你多努力的进行测试,仍然不能制作出无缺陷的软件,现实世界中的测试极少能证明软件刀枪不入,而仅仅能证明它是可以用的而已。软件测试类型可以分为:单元测试。通常用来表示测试代码的一个模块,它实际上描述的是对原子单元的测试。组件测试。用于验证由一个或多个单元组成的完整组件的行为。集成测试。对放入一个系统的组件的组合进行测试,确保它们之间正确的相连。回归测试。对软件或其环境进行修正或休整之后重新进行的测试。负载测试。确保代码可以处理抛给它的预期的数据量,负载测试可检查程序是否如预期的是”向上扩展的“。压力测试。在很短的时间内向代码抛出大量的数据,以观察代码会出现什么状况。其目的是确保代码在受到真正冲击时不会乱作一团。这种测试通常用在多线程或实时系统中。疲劳测试。与压力测试相似,它关注代码在较高的负载下运行一段很长的时间,以确定任何会在执行大量的操作之后会出现的性能问题。它可以发现一些其他测试无法检测到的缺陷。可用性测试。通常在可控度很到的可用性实验室中进行。我们在进行你给软件测试设计时,主要有以下两种思路:黑盒测试。也称功能测试。白盒测试。也称结构测试。其工作量非常大,并且成本要远远高于黑盒测试。黑盒测试关心的是代码缺少什么,白盒测试关心的是代码在功能上的缺陷。编写一个全面的测试用例,其中每个测试针对代码的一个方面,15个重复指出相同缺陷的测试,远不如15个分别指出15个不同缺陷的测试有用。你必须选择那些最有可能找出软件缺陷的测试,而不是执行那些只是重复的指出很少几个相同问题的测试。与目的不符的测试,可能只会把错误的事情变得更糟。在进行黑盒测试时,可以使用以下一些测试用例:一些良好的输入。一些不好的输入。边界值。随机数据。零。你所编写的单元测试的质量,在很大程度上取决于你要测试的代码接口的质量。编写清晰的API,减少对其他代码的依赖,以及断开对其他组件的任何写死的链接。为测试而设计,就会以一种合理的、可理解的和可维护的方式来构造代码,你将减少组件的耦合度并增强其聚合度。应用以下原则,有助于编写具有非常好的可测试的代码:使各个代码部分都自包含,而不要与外部世界建立未经说明的或缺乏实质的依赖关系。不要依赖全局变量,将这种状态集中到一个作为自变量传递的共享结构中。限制你的代码的复杂度,把代码分为较小的、可以理解的并且可以单独进行测试的代码块。保持代码的可观测性。尽可能让你的代码测试自动的进行,这要比手动测试更快、更容易、更安全,而且可以使测试更有规律的进行。将自动的进行单元测试作为你的构建过程的一部分。当我们遇到一个程序故障时,可以按照以下步骤进行处理:注意当时你做了什么,以及是什么操作引发了这个故障。再试一次,查明这个问题是否是可重复的,出现的频率如何,以及是否与同事进行你给的任何其他活动相关。完整的描述故障,要非常具体。将这些描述记录下来。编写可以检测出这个缺陷的最简单的测试用例。难以解决的故障是不规则的,甚至是随机的,从而很难说明它们的特征。你必须运用系统化的方法来查找缺陷,你还必须运用系统化的方法来管理和处理缺陷。缺陷跟踪系统是我们在管理缺陷是使用的一个重要武器,这个工具是一个专用的数据库,它的接口对每个参与测试过程的人都可见。通常的操作包括:报告故障。分配责任。确定报告的优先级。标记为已修正。结束报告。查询数据库。修改条目。任何程序员编写测试都有一个秘密的目标:证明他的代码是正确的,而不是找出它有问题的地方。优秀的程序员:1. 为他们所写的代码编写测试;2. 在微观层次上进行测试,以便宏观层次的测试不会被愚蠢的编码错误妨碍;3. 关心产品质量并为之负责,在整个测试工作中发挥他们的作用。糟糕的程序员:1. 不认为测试是软件开发的一个重要和必要的部分,觉得这是别人的工作;2. 将未经测试的代码提交给QA部分,当测试发现有缺陷的行为时感到很意外;3. 由于问题发现的很晚,所以让自己生活变得更加复杂不及早做充分的测试,就会被许多难以定位iede缺陷击倒。第九章 寻找缺陷bug是构建软件时不可避免的黑暗面,是生活的一个朴素的真相。在很多情况下,犯错误只是因为没有专注于正在做的事情,大多数bug都是粗心大意造成的。软件的bug可以分为以下几个主要的类别:编译失败。这是你遇到的错误类型中最好的一种,检测到缺陷所需的时间越长,修正它们的成本就越高。运行时崩溃。非预期的行为。这是真正难以处理的错误你的程序并没有崩溃,而只是准备跳下悬崖。运行时错误可以分为以下几类:句法错误。为了避免这种错误,最简单的方式就是将所有的编辑器警告都打开。构建错误。构建错误虽然本省不是运行时缺陷,但是却只在运行时出现。明智的做法是对项目进行彻底的清理,然后从头开始重新构建它。基本的语义bug。这种bug一般都可以使用静态分析工具发现。语义bug。常见的语义缺陷包括:段错误。也称为“保护缺陷”。源自程序访问那些并没有分配给它们的存储单元,涉及指针的输入错误或糟糕的指针算法,都非常容易造成这种错误。内存溢出。由于写入哪些已为你的数据结构分配的内存造成的。内存溢出是一个常见的问题,很难检测到,尽可能使用安全的数据结构,以使你与这种灾难的可能性绝缘。内存泄露。在哪些不具有垃圾回收功能的语言中常常发生。如果你忘记释放内存,你的策划那个徐将慢慢消耗掉越来越多的稀缺的计算机资源。内存耗尽。数学错误。程序暂停。一般归因于糟糕的程序逻辑。有些程序员天生就能够更加专注的执行任务,他们可以一遍将精力集中在正在编写的代码的细节上,一边还在头脑中保持全局的观念。这就是调试的艺术,它在很大程度上是一种需要学习的技能。有太多的程序员试图通过胡乱的修改代码来修正缺陷,而从不认真的思考他们正在做什么,这样做的后果极少是有用的。为“松散”的调试设定一个合理的时间限制,如果调试没有成功,就采取更加系统的方法。即使缺陷很容易找到,如何修正它也不一定是显而易见的。一种系统化的经过深思熟虑的调试技术认为移除bug分为两个不同的方面:找出造成bug的缺陷;对缺陷进行修正。调试时的一些原则:查找一个缺陷的难易程度,取决于你对隐藏这个缺陷的代码的熟悉程度。调试的难易度还取决于你对执行环境的控制能力,即你对运行中程序有多大的控制权,以及是否可以方便的查看它的状态。不信任任何人的代码,并且抱有一种健康的怀疑态度。如何搜寻bug,可以分为以下几种情况:编译时错误。当你的构建失败时,先看看第一个编译错误,这个错误的可信度要远远高于其后的消息。运行时错误。如果你的程序中包含一个bug,那么很可能是你的代码中某个你认为成立的条件其实并不成立,唯一合理的方式是系统的执行这个过程。调试是一项系统的工作,需要慢慢的接近缺陷所在的位置。不要把它看做是一个简单的猜谜游戏。确定故障。调试将从你注意到程序没有执行它应该执行的操作开始。使故障重现。确定缺陷位置的第一步,是查明如何可靠的使它再次出现。定位缺陷。仅仅因为故障出现在某个模块中,并不一定表示这个模块有问题。从你知道的地方开始,例如程序崩溃的位置,然后从那里沿控制流向后查找发生故障的原因。理解问题。缺陷通常都很隐蔽,代码会执行它应该执行的操作,以及那些你
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 帮别人车辆过户委托书
- 文秘工作心得体会
- 2024年湘中幼儿师范高等专科学校辅导员考试真题
- 历史城市文化政策研究基础知识点归纳
- 2025年产品开发和设计阶段试题
- 智慧空间下高校学生未来学习需求分析
- 特种纸企业经营管理方案
- 2025至2030年中国电动遥控双开门控制器行业投资前景及策略咨询报告
- 2025至2030年中国珐琅门行业投资前景及策略咨询报告
- 小学六年级作文写事
- 四川省英语高考试题及解答参考(2025年)
- 《传染病防治法》课件
- 中南运控课设-四辊可逆冷轧机的卷取机直流调速系统设计
- 风景园林专业中级职称《法律法规及技术标准》考试题及答案
- DB11T 893-2021 地质灾害危险性评估技术规范
- 呼吸系统测试题(含参考答案)
- 欧洲文明概论学习通超星期末考试答案章节答案2024年
- 民兵知识小常识
- 山西省大同市平城区2023-2024学年六年级下学期期末考试英语试卷
- 2023年高考真题-地理(河北卷) 含答案
- 大学本科教学指导委员会章程
评论
0/150
提交评论