[笔记]代码大全(二)

作者:frank 发表日期:2016-09-12 20:08:22 更新日期:2016-09-12 20:08:22 分类:猿文色

摘要

读书笔记之代码大全二。

正文

伪代码

创建类的步骤:

  1. 创建类的总体设计。
  2. 创建类中的子程序。
  3. 复审并测试整个类。(缺乏类的设计的尝试!!)

创建子程序的步骤:

  1. 设计子程序。
  2. 检查设计。
  3. 编写子程序代码。
  4. 复审并测试代码。

伪代码写法:

  1. 避免使用目标编程语言的语法元素。
  2. 在意图层次上编写伪代码(描述解决问题的意图)。
  3. 不断精化伪代码,加入更多细节,直到看起来已经很容易直接写出代码为止。

有些子程序无法依靠伪代码完成!!

设计子程序

子程序

  1. 子程序要隐藏的信息。
  2. 输入。
  3. 输出。
  4. 前条件是否成立。
  5. 后条件是否成立。

子程序命名:如果你在给程序起个好名字的时候犯难,通常表明这个子程序的目标还没明确。 单元测试:如何测试子程序???测试子程序的每一条执行路径。

优先在标准库中搜索可用的功能。 考虑错误处理。

对于大多数系统而言,子程序的效率并不十分紧要。根据所处的系统的资源及速度的目标来设计子程序。除了某些特殊情况,在子程序上为效率卖力通常是白费功夫。最主要的优化还是完善高层设计。

研究算法和数据类型。

子程序的头部注释,如果很难写出来,就要考虑子程序的设计是否合理。

在伪代码中尝试一些想法,留下最好的想法(迭代)。

通过伪代码编写代码的步骤:

  1. 写出程序的声明。
  2. 编写第一条和最后一条语句,将伪代码转换为注释。
  3. 每条注释下填充代码。
  4. 检查代码。
  5. 收尾工作。

只是能写出一个可以工作的子程序是不够的。理解它为什么可以工作,直到弄明白为止!!

脱离那种先东拼西凑,通过运行来看代码是否工作的怪圈!!

伪代码编程过程的替代方案:

  1. 测试先行开发。
  2. 重构。
  3. 契约式设计。
  4. 东拼西凑。

变量

避免隐式变量声明。

变量初始化原则:

  1. 在声明的时候初始化。
  2. 在靠近变量第一次使用的位置初始化(就近原则,即把相关的操作放在一起)。
  3. 理想情况下,在靠近第一次使用变量的位置声明和定义该变量。
  4. 在可能的状况下使用const或final。
  5. 在类的构造函数中初始化类的数据成员。
  6. 检查输入参数的合法性。

作用域:衡量变量知名度的方法。 作用域或者可见性指的是变量在程序中的可见和可引用的范围。

原则:

  1. 使变量的引用局部化,集中化。
  2. 尽可能缩短变量的存活时间,跨度小。

减小作用域的一般原则:尽量使变量局部化。 持续性:准备抛弃变量时为其赋上不合理的数据(比如循环中的计数器)。编写代码前假设数据并没有持续性。养成在使用所有数据之前声明和初始化的习惯。 绑定时间:把变量和它的值绑定在一起的时间,绑定越晚越有利。 使用具名常量在很多方面好于使用神秘数值。

数据类型与控制结构之间的关系:

  1. 序列型对应顺序语句。
  2. 选择型数据对应if或case语句。
  3. 迭代型对应循环语句。

为变量指定单一用途:

  1. 每个变量都只有单一用途。
  2. 避免让代码具有隐含含义(混合耦合,pageCount大于等于0时表示页码,小于0时却代表错误,整型客串为布尔型)。
  3. 确保使用了所有已声明的变量。

变量命名规则:完全准确的描述出该变量所代表的事物。一个好的名字通常反映的是问题,而不是解决方案(以问题为向导)。 变量名的长度:8~20字符。太长或太短都要再次检查代码,确保名字含义足够清晰。

缩写的一般指导原则:

  1. 使用标准的缩写。
  2. 去掉所有的非前置元音(computer变为cmptr,screen变为scrn)。
  3. 去掉虚词and,or,the等。
  4. 使用每一个单词的第一个或前几个字母。
  5. 保留每个单词的第一个和最后一个字母。
  6. 使用名字中的每一个重要单词,最多不要超过3个。
  7. 去掉无用的后缀——ing,ed等。
  8. 不要改变变量的含义。

作用域对变量名的影响:当把一个变量名取得很短的时候,说明该变量代表了一个临时变量,作用域非常有限。

  1. 对全局变量的名字加以限定词。
  2. 变量中的计算值限定词放在变量名的最后(Total, Sum, Max, Min, String, Pointer等等),Num是个特殊的限定词,放到开始位置代表一个总数,放到最后代表下标(尽量避免使用Num,使用Count或Total代替)。
  3. 使用对仗词(begin/end, first/last)。

为特定类型的数据命名:

  1. 简单循环ijk,复杂循环避免。
  2. 为状态变量命名。
  3. 为临时变量命名(警惕临时变量)。
  4. 为布尔变量命名(done, error, found, success, ok或者前置is)。

总的来说变量名包含了3类信息:

  1. 变量的内容。
  2. 数据的种类(类型)。
  3. 变量的作用域(私有,成员,全局)。

应该避免的名字:

  1. 避免使用令人误解的名字。
  2. 避免使用具有相似含义的名字。
  3. 避免使用具有不同含义却又相似名字的变量。
  4. 避免使用发音相近的名字。
  5. 避免在名字中使用数字。
  6. 避免拼错单词。
  7. 不要仅靠大小写来区分变量名。
  8. 避免使用多种自然语言。
  9. 避免使用关键字。
  10. 避免包含易混淆的字符。

基本数据类型

建议:

  1. 避免使用神秘数值(程序中出现的没有经过解释的数值文字量)。
  2. 如果需要,可以使用硬编码的0和1。
  3. 预防除零错误。
  4. 使类型转换变得明显。
  5. 避免混合类型的比较。
  6. 注意编译器警告。

整型:

  1. 检查整数除法。
  2. 检查整数溢出。
  3. 检查中间结果溢出。

浮点数:

  1. 避免数量级相差巨大的数之间加减运算(排序,从最小的数开始运算)。
  2. 避免等量判断(设置可接受的精度范围)。
  3. 处理舍入误差。

字符和字符串:

  1. 避免使用神秘字符和神秘字符串(使用具名常量)。
  2. 在程序的生命周期中尽早决定国际化及本地化策略。
  3. 如果需要支持多语言,请使用Unicode。

布尔变量:

  1. 用布尔变量对程序加以文档说明(使判断的含义变得明显)。
  2. 用布尔变量简化复杂的判断。

枚举类型:

  1. 提高可读性。
  2. 提高可靠性。
  3. 简化修改。
  4. 作为布尔变量的替换方案。
  5. 检查非法数值。
  6. 定义第一项和最后一项用于检测循环边界。
  7. 把第一项作为非法值。

不常见的数据类型:

结构体,指针,全局数据。

控制结构

直线型代码

顺序相关的语句:如果语句的执行有前后依赖关系,使依赖变得非常明显。

  1. 组织更好的代码。
  2. 使子程序名凸显依赖关系。
  3. 利用子程序参数明确依赖关系。
  4. 使用注释说明。
  5. 用断言或者错误处理代码来检查依赖关系。

顺序无关的语句:就近原则,把相关的操作放在一起。

  1. 使代码易于自上而下的阅读。
  2. 把相关的语句组织在一起。

条件语句

if-else语句:

  1. 首先写正常代码路径,再处理不常见情况。
  2. 确保对于等量的分支是正确的。
  3. 把正常情况的处理放到if后面。
  4. 让if后面跟随一个有意义的语句。
  5. 尽量考虑到else语句。
  6. 测试else语句的正确性。
  7. 检查if和else是不是弄反了。

if-then-else语句:

  1. 利用布尔值简化复杂的判断。
  2. 把正常的情况放在最前面。
  3. 确保所有的情况都考虑到了。
  4. 尽量替换为case语句。

case语句:选择最有效的排列顺序。

  1. 按字母顺序。
  2. 正常情况放在前面。
  3. 按执行频率排列。窍门:1.简化每种情况对应的操作。2.不要刻意制造一个变量来使用case语句。3.default语句只用于检查真正的默认情况。4.使用default来检测错误。

循环语句

带退出循环结构要比其他循环结构更接近人类思考迭代型控制的方式。

while循环可以灵活的设置终止条件,for循环适合执行次数固定的循环,foreach适合对数组或对象中的每个元素执行操作。 循环体:把循环的内务操作(housekeeping,控制循环的语句,i++)要么放在循环的开始,要么放在循环的末尾。一个循环只做一件事情。 退出循环:不要为了退出循环而胡乱改动循环的下标,避免出现依赖于循环下标最终取值的代码,考虑使用安全计数器。 提前推出循环:小心那些有很多break散步其中的循环,在循环开始出用continue进行判断,如果需要支持就使用带标号的break结构。 使用循环变量:在嵌套循环中使用有意义的变量名来提高其可读性。把循环下标变量的作用域限制在本循环内。 循环应该有多长:15-20行。循环限制在3层以内。长循环移到子程序中。让长循环格外清晰。

不常见的控制结构

子程序中多处返回:

  1. 如果能增强可读性,那么就使用return。
  2. 用防卫子句来简化复杂的错误处理。
  3. 减少每个子程序中 return 数量。

递归:

  1. 确认递归能够停止。
  2. 使用安全计数器防止出现无穷递归。
  3. 把递归限制在一个子程序中。
  4. 留心栈空间。 5 .不要用递归去计算阶乘或者斐波那契数列(糟糕的做法)。
  5. 用递归能做到的,用栈和循环也能做到。

goto语句。

表驱动法:一种编程模式,从表里查找信息而不使用逻辑语句。

两个问题:1.怎样从表中查询条目。2.应该在表中存些什么数据。

从表中查询数据的方法:

  1. 直接访问。
  2. 索引访问。
  3. 阶梯访问。

一般控制问题

布尔表达式:

  1. 用true或false做布尔判断。
  2. 隐式的比较布尔值与true和false。
  3. 拆分复杂的判断并引入新的布尔变量。
  4. 把复杂的表达式做成布尔函数。
  5. 编写肯定的if语句。
  6. 用狄摩根定理简化否定的布尔判断。
  7. 用括号使布尔表达式更清晰。
  8. 短路求值与惰性求值。
  9. 按照数轴的顺序编写数值表达式。

复合语句(语句块):1. 把括号一起写出。

空语句:1.为空语句创建一个DoNothing的宏或者内联函数。

驯服危险的深层嵌套:case, break,...简化深层嵌套。

结构化编程:一个应用程序应该只采用一些单入单出的控制结构。一个结构化程序应该按照一种有序且有规则的方式执行,不会做不可预知的随便跳转。结构化编程的中心论点是任何一种控制流都可以由顺序、选择、迭代三种结构生成。

协同构建

结对编程,正式检查,走查,代码阅读,公开演示

开发者测试

测试分类:单元测试,组件测试,集成测试,回归测试,系统测试。 测试分类:黑盒测试(测试人员不了解测试对象的内部工作机制),白盒测试。 测试先行:测试先行是非常好的通用方法。

开发者测试的局限性。

测试技巧锦囊:结构化的基础测试,需要测试程序中每一天语句至少一次,算一算有多少条通过程序的路径,然后据此开发出能通过每条路径的最少数量的测试用例。代码覆盖测试和逻辑覆盖测试。数据流测试,已定义,已使用,已销毁,已进入(进入一个子程序),已退出。等价类划分。猜测错误。边界值分析。复合边界值(经过运算的边界值)。

典型错误:绝大多数错误往往与少数几个具有严重缺陷的子程序有关。 净室开发技术。 留意测试本身的错误。 保留测试记录。 个人测试记录。

调试

低效率的调试方法:凭猜测找出缺陷,不理解问题,特殊对待。

科学的调试方法:

  1. 通过可重复的试验收集数据。
  2. 构造一个假说。
  3. 设计一个实验证明或反证这个假说。
  4. 重复上述步骤。

如果一个错误无法重现,通常是一个初始化错误,或者是一个同时间有关的问题,或者是悬空指针问题。 不时潜入头脑中的焦虑是一个明确的信号:到了该休息的时候了。

修正缺陷:

  1. 动手之前先理解问题。
  2. 理解程序本身,而不仅仅是问题。
  3. 验证对错误的分析。
  4. 放松一下(匆忙动手解决问题是你所能做的最低效的事情之一)。
  5. 保留最初的源代码。
  6. 治本,而不是治标。
  7. 修改代码时一定有恰当的理由(不要随机的修改代码)。
  8. 一次只做一个改动。
  9. 检查自己的改动。
  10. 增加能暴露问题的单元测试。
  11. 搜索类似的缺陷。

重构

根据项目规模的不同,典型的项目花在编码、调试和单元测试的时间会占到整个项目的30%~65%不等。即使是管理完善的项目,每个月也会有1/4的需求发生变化。

区分软件演化类型的关键就是程序的质量在这一过程中是提高了还是降低了,如果将程序的修改看作是对原始设计的升华,程序质量则会提高。软件演化的哲学:如果能在开发过程中意识到软件演化是无法避免的且具是有重要意义的现象,并对其细加谋划,就会使这一过程有益于你的开发。一旦有机会重新审视你的程序,就要用自己全部所学去改进它。

软件演化的基本准则是提高程序的内在质量。