第一部分 打好基础 Laying the Foundation
- 第一章 欢迎进入软件构建的世界 Welcome to Software Construction
- 什么是软件的构建
- 定义问题
- 需求分析
- 规划构建
- 软件架构 或者 高层设计
- 详细设计
- 编码与调试
- 单元测试
- 集成测试
- 集成
- 系统测试
- 保障维护
- 总结
- 软件构建是软件开发中惟一不可缺乏的部分,也就是必须完成的部分
- 软件的构建主要包括:详细设计、编码调试、集成和开发者测试(单元测试和集成测试)
- 你对软件构建的理解程度决定 程序员的优秀程度
- 第二章 用隐喻来更充分地理解软件开发 Metaphors for a Richer Understanding of Software Development
- 第三章 三思然后行:前期准备 Measure Twice, Cut Once: Upstream Prerequisites
- 第四章 关键的『构建』决策 Key Construction Decisions
第二部分 建立高质量的代码 Creating-High Quality Code
- 第五章 软件构建中的设计 Design in Construction
- 第六章 能够工做的类 Working Classes:抽象是以简化方式看待复杂操做的能力
- 6.1类的基础:抽象数据类型 ADTs
- ADT,abstract data type 抽象数据类型。它指的是一些数据及对这些数据的操做的集合。这里的"数据",不只仅是数学上或者软件工程中的数据,而是现实世界中能够操做的实体
- ADT 的好处:
- 隐藏实现细节(可能会有后续操做)
- 容易改动(更改数据结构,优化提升性能)
- 让接口提供更多信息(经过名称)
- 可读性提升
- 不须要屡次传值(相关操做须要用到变量都放在ADT里面了)
- 把常见的底层数据类型(栈 队列)建立为ADT并使用
如出场演员名单(底层数据类型是列表)java
- 对于应用层面上ADT,最好在原有ADT的基础上建立一个针对现实世界问题的抽象层次。
- 简单的事情能够抽取成ADT(方便扩展后续操做)
- 在支持面向对象的语言,ADT能够用本身的class(类)实现。class=ADT+继承+多态
- 6.2良好的类接口:用接口去展现抽象,确保细节隐藏在抽象背后
- 接口中的每一个子程序都朝着这个一致的目标而工做
- 类的接口要展现一致的抽象层次,一个类只能实现一个ADT,否则就要拆分
- 要理解类要抽象出什么功能,避免把使用的类库或者容器类暴露出来
- 尽量让接口可编程(programatic,编译器强制要求),而不是表达语义(sematic,经过方法名和注释)。
好比多个类的初始化有前后顺序;一个类没有初始化调用会报错c++
- 扩展的时候要注意新增公用方法的 抽象的一致性
- 不要对类的使用者作任何假设,接口已经隐含了契约(接口已经提供了调用的条件说明)
- 语义上的封装比语法上的封装要困难(公用接口不要暴露内部实现和数据)
P142 不少例子程序员
- 封装和抽象要么二者皆有,要么所有没有
- 6.3有关设计和实现的问题:包含/继承/成员函数/数据成员/类之间的耦合性
- 包含(has a "有一个的关系"):数据成员的限制:7-+2
数据成员都是基本数据类型,数据成员不超过9;数据成员都是复杂对象,数据成员不超过5算法
- 继承(is a “是一个的关系”)(使用时会增长复杂度,有违软件的技术使命-管理复杂度的)
- 要考虑方法和属性对派生类是否可见,方法是否要有默认的实现,是否能够覆盖?
- 继承要符合里氏替换原则:对于基类定义的接口,在派生类的语义应该是相同的
- 不要覆盖不可覆盖的方法(不要新建一个与基类的private相同的方法)
- 只有一个派生类,可能犯了提早设计的毛病
- 继承不要超过2-3层,派生类总数不超过该7+-2个;
- 尽量让数据让数据时private,由于继承会破坏封装
- 若是多个类共享数据而非行为,建立这些类包含共用对象
- 若是多个类共享行为而非数据,在基类定义接口,继承基类
- 若是多个类共享行为和数据,在基类定义接口和数据成员,继承基类
- 当你想由基类控制接口时,用继承,由本身控制接口,用包含
- 成员函数和数据成员:
- 减小如下数字的数量
- 所实例化对象的种类
- 调用实例化对象的子程序的数量
- 调用由其余对象返回对象的子程序数量
- 子程序的数量
- 构造函数
- 6.4建立类的缘由
- 对现实对象的建模
- 对抽象对象的建模(如shape就是抽象对象,得出恰当的抽象对象很重要)
- 下降复杂度(调用类的接口不用关心实现细节)
- 隔离复杂度
- 隐藏实现细节
- 限制变化的影响范围
- 隐藏全局数据
- 让参数传递更流畅
- 建立中心控制点
- 让代码重用
- 为程序族作规划
- 把相关操做放在一块儿(子程序的组合)
- 实现特定的重构
- 6.5与具体编程语言有关的问题
- 6.6超越类:包
- 类的质量核对表P157-P158
- 第七章 高质量的子程序 High-Quality Routines
- 7.1 建立子程序(routines)的正当理由:提升程序管理能力,包括提升可读性、可靠性和可修改性,节省代码空间是次要,或者是反作用(side effect)。
- 下降复杂度
- 引入中间,易懂的抽象
- 避免重复
- 支持子类化(subclassing)
- 隐藏顺序
- 隐藏指针操做
- 提升可移植性
- 简化布尔判断
- 改善性能
- 确保全部程序都很小
- 7.2 在子程序层上设计(Design at the Routine Level):抽象和封装理念适合类层次的设计,内聚性适合子程序设计
- 内聚性(cohesion):指子程序中各类操做之间联系的紧密程度。(一个子程序就干一个活)
- 功能内聚性:一个子程序只干一件事情
- 如下是不够理想的内聚性,可是有做用的。
- 顺序上的内聚性:子程序包含按特定顺序执行的操做,这些操做共享数据,并且只有在操做所有执行完毕的时候才达到一项完整的功能
- 通讯上的内聚性:指一个子程序内的不一样操做用一样的数据,但不存在任何的联系
- 临时的内聚性:一些由于须要同时操做而放在一块儿的操做
startUp()数据库
- 如下是不可取的内聚性
- 过程内聚性:把一组操做放在子程序中并按照特定顺序执行,除此以外没有其余彼此的联系
- 逻辑上内聚性(实际上是缺少逻辑的内聚性):把若干操做放入同一子程序,经过传入的控制符执行不一样的操做
- 若是子程序仅有由一系列if else语句以及其余子程序语句组成,这样的逻辑上内聚性的子程序是能够的,就是这个子程序只发出各类指令,不进行任何的处理,那么他就是一个事件处理器(event handler)
- 巧合的内聚性:子程序的操做之间没有任何的 联系
- 7.3 好的子程序的名字 Good Routine Names
- 描述子程序所作的全部的事
- 避免使用无心义、模糊或者表达不清的动词
event handling
事件处理除外
handleCalculation()
PerformServices()
OutputUser()
ProcessInput()
DealwithOutPut
编程
outPut1()
; outPut2()
小程序
- 根据需求肯定子程序的名称长度
- 函数命名时要对函数的返回值有所描述
printer.isReady()
customerId.next()
数组
- 给子程序命名时要使用语气强烈的动词加宾语的形式,在面向对象语言中,不用再过程(Procedure)名中不用加入对象名(宾语)
document.print()
orderInfo.check()
安全
- 准确使用对仗词
add/remove
insert/delete
start/stop
up/down
begin/end
increment/decrement
open/close
get/set
first/last
old/new
min/max
show/hide
create/destory
lock/unlock
source/target
get/put
ruby
- 为经常使用操做确立命名规则
- 7.4 子程序能够写多长 How long can a Routine be
- 虽然有不少研究可是仍然没有一个公认的说法,通常是50-200行左右,能够从子程序的内聚性、嵌套层次、变量数量、决策点(desicions points)和注释来决定子程序的长度,当超过200行,就要考虑在可读性上问题了。
- 7.5 如何使用子程序参数 How to Use Routine Parameters
- 按照输入-修改-输出的顺序排列参数:暗含了操做数据的顺序
- 若是子程序用了相似的参数,参数的排列顺序应该一致
- 使用全部的参数,没有用的要撇除
- 把状态或者出错变量放在最后,由于它们只是程序的附属
- 不要把子程序参数当作工做变量
对输入参数进行操做,并将其做为返回值返回结果。
* 在接口中对参数的假定加以说明,在接口文档说明
* 参数是仅用于输入,要被修改,仍是仅用于输出
* 表示数量的参数单位(秒/分)
* 没有用枚举类型的话,应该说明状态码和错误码的含义
* 所能接受的数据范围
* 不能接受的特定数值
* 把子程序的参数个数限制在7个之内
* 如何一直须要传递不少参数,说明子程序之间的耦合太过紧密。若是须要向不少不一样的子程序传入相同的数据,就把这些子程序组成一个类,并把那些常用的数据做为类的内部数据
* 考虑对参数使用某种输入、修改、输出的命名规则
> i_xxx
,m_xxx
,o_xxx
或者 Input_xxx
,Modify_xxx
,Output_xxx
* 为子程序传递用以维持其接口抽象的变量和对象
* 假如常常须要修改子程序的参数表,且都是来自同一个对象,那就传递整个对象
* 若是只是为传递几个特定的数据,把数据填入对象,再到子程序读取这些数据,那就只传递数据的值
- 使用具名参数?
- 确保实际参数与形式参数相匹配?
- 7.6 使用函数时要特别考虑的问题
- 函数是有返回值的子程序,过程是指没有返回值的子程序
- 设置函数的返回值
- 检查全部可能的返回路径
- 假如须要返回有关的数据,那就应该做为类的成员保存起来,而不是做为局部数据的引用或者指针返回。
- 7.7 宏子程序和内联子程序 Macro Routine And Inline Routines?
- 把宏表达式整个都包含在括号内
- 把包含多条语句的宏用大括号括起来
- 用给子程序命名的方法给展开后代码形同子程序的宏命名,以便须要能够用子程序来替换宏。
- 高质量的子程序核对表P185
- 第八章 防护式编程 Defensive Programming:子程序应该不因传入错误的数据而被破坏,哪怕是由其余子程序产生的错误的数据。即核心思想是程序都是有问题,都是要被修改。
- 8.1 保护程序免遭非法输入数据破坏 Protecting your program from invalid inputs
- 检查全部来源于外部数据的值:从文件、网络、用户或者其余外部接口得到的数据应该检查其有效性
- 检查子程序全部输入参数的值
- 决定如何处理错误的输入数据
- 8.2 断言 Assertions
- 在开发期间使用的,让程序运行时进行自检的代码,一个断言通常包含两个参数,一个布尔表达式,一个断言为假时显示的消息。断言程序执行的前条件和后条件
- 用断言检查一下假定:
- 输入参数或者输出参数在预约范围内
- 子程序开始(结束)文件或者流处于开启或者关闭的状态
- 子程序开始(结束)文件或者流读写的位置位于开头或者结尾
- 指针不为空
- 传入子程序的数组或者其余容器至少能存储X个数据元素
- 表已经初始化,存储着真实的数据
- 仅用于输入的变量的值没有被子程序改变
- 子程序开始或者结束的时候,某个容器为满或者为空
- 高度优化的子程序与运行缓慢但逻辑清晰的子程序运行结果一致
- 对于高健壮性的代码,应该先断言再处理错误
- 8.3 错误处理技术 Erro-Handling Technique:断言处理不该该发生的错误,错误处理技术处理预料中可能发生的错误。
- 返回中立值
- 数值计算返回0
- 字符串操做返回空字符串
- 指针操做返回空指针等
- 换用下一个正确的数据
温度计读取数值失败,能够等下一次如1/100秒读取
* 返回前次相同的数据
> 上面温度计的例子一样适用
* 换用最接近的合法值
>获得字符串长度小于0,那返回0
* 把警告信息写到日志
* 返回一个错误码
* 调用处理错误的对象或者子程序
* 健壮性 vs 正确性 robustness vs correctness
* 高层次设计对错误处理方式的影响
- 8.4 异常Exceptions:是把错误或者异常事件传递给调用方代码的特殊手段
- 用异常通知其余的程序,发生了不可忽略的错误
- 与断言类似,都是用来处理罕见甚至不可能出现的状况???
- 是处理意外的有效途径,可是增长复杂度
- 不能用异常来推卸责任,明确是由自身处理异常仍是由调用方处理异常
- 避免在解构函数和构造函数抛出异常,除非在同一地方进行捕获
- 在恰当的抽象层次抛出异常,就异常应于当前接口的抽象层次一致,避免暴露实现细节和内部信息
- 避免使用空的catch语句,除非将catch的异常文档化
- 了解函数库可能抛出的异常、
- 考虑创造一个集中的异常报告机制,就是对于异常进行统一的格式化并记录和存储
- 把项目中对异常的使用标准化
- 能够定义项目特定的异常类,记录日志、报告错误的集中起来和标准化
- 规定何种场合异常时须要局部处理
- 规定何种场合异常只能抛出,不能局部处理
- 考虑异常的替换方案
- 8.5 隔离程序,使之包容由程序错误形成的伤害Barricade Your Program to Contain the Damage Caused by Errors
- 隔栏是一种容错的策略 damage containment strategy
- 能够在类的层面上采用这种方法,在类的公有方法假定输入的数据时不安全的,对数据进行检查并清理,以后再将数据传给私有方法,类的私有方法假定数据都是安全的
- 隔栏外部的程序使用错误处理技术,在那里对数据的假定是不安全的。内部的程序使用断言技术,这样若是隔栏内的出现错误的数据,就是程序上的错误而非数据上的错误
- 8.6 辅助调试的代码 Debugging aids
- 不要把自动地把产品版的限制强加于开发版上:开发版能够花费更多的资源,容许运行缓慢,容许暴露不安全的操做
- 尽早引入辅助代码
- 采用攻击式编程 offensive programming
- 确保断言使程序停止,及时修复错误
- case语句的default分支或者else分支产生严重的错误以至不被忽视
- 计划移除调试辅助的代码
- 8.7 肯定在产品中该保留多少防护式代码 Determine How much Defensive Programming to Leave in Production code
- 保留那些检查重要错误的代码
- 去掉检查细微错误的代码
- 去掉能够致使程序硬性崩溃的代码:虽然便于调试,可是用户体验差
- 为你的技术支持员记录错误信息
- 确认留在代码中的错误信息是友好的(friendly):就是要严谨,正式
- 8.8 对防护式编程采起防护姿态 Being Defensive about Defensive Programing
- 防护式编程增长复杂度
- 防护式编程由于要检查参数使程序运行缓慢
- 核对表 P211
- 第九章 伪代码编程过程 The Pseudocode Programming Process
- 9.1 建立类和子程序的步骤概述 Summary steps of Building Classes and Routines
- 图9.1 at p216:
- 建立一个类的步骤 Steps in Creating a class
- 建立类的整体设计 具体参考第六章 能够工做类
- 定义类的职责
- 定义类要隐藏的"秘密"
- 定义类的接口所表明的抽象概念
- 决定这个类是否要从其余类派生出来
- 决定这个类是否能够被派生,便是否能被继承
- 指出类的关键公用方法
- 标识并设计出类所须要的重要数据成员
- 建立类的子程序 Steps in Building a routine
- 图9.2 at p217
- 子程序的种类:成员访问子程序 (accessor routine ),转发到其余对象(pass-throughs)的子程序
- 步骤:设计子程序->检查设计->编写子程序的代码->检查代码
- 复审并测试整个类 :在子程序建立的同时通过测试,在整个类能够工做后,应该再对总体进行复查和测试,以便于发如今子程序独立测试层次上没法发现的问题
- 9.2 伪代码 Pseudocode for Pros
- 伪代码:描述 算法、子程序、类或者完整程序的工做逻辑、非正式的、相似英语的记法
- 伪代码的注意事项:
- 用相似英语的语句来精确描述特定的操做
- 避免使用目标编程语言的元素,伪代码是比代码自己略高的设计层次,使用目标编程语言的元素会下降设计层次
- 在本意 intent 层面编写伪代码
- 在足够低的层次编写伪代码,以即可以近乎转化为代码
- 好的伪代码能转换为注释
- 伪代码的好处:
- 伪代码使得评审更容易
- 伪代码支持反复迭代精化思想:自顶向下,逐层拆解问题,解决问题
- 伪代码使变动更加容易
- 伪代码能使给代码做注释的工做量减小
- 伪代码比其余设计形式的文档更容易维护
- 9.3 经过伪代码编码过程建立子程序
- 设计子程序 Design the Routine
- 检查先决条件:子程序工做是否认义好,是否是与总体设计相匹配。是否项目必需的
- 定义子程序的解决的问题:
- 子程序要隐藏的信息
- 子程序的输入
- 子程序的输出
- 调用子程序前确保有关的前条件成立(输入数据在特定范围内,流已经初始化等)
- 在子程序将控制权交回调用程序前,确保后条件成立(输出数据在特定范围内,流已经关闭)
- 为子程序命名
- 决定如何测试子程序
- 在标准库搜索可用的功能:重用好的代码,不重复造轮子
- 考虑错误处理
- 考虑效率问题:
第一种状况绝大数系统而言,效率并非十分紧要。另外一种状况是对少数系统而言性能很是重要。在除了上述两种状况,在子程序效率的优化是白费功夫的,由于主要的优化是在于完善高层的设计
* 研究算法和数据类型
* 编写伪代码:先写头注释 head comment,再写伪代码
* 考虑数据
* 检查伪代码:确认很容易,很天然地理解子程序作些什么以及怎样作
* 在伪代码中试验一些想法,留下最好的想法(迭代):伪代码试验想法成本比代码低。
> 用伪代码反复描述这个子程序,直到伪代码写出句子已经足够简单,你能够把伪代码直接变成代码文档为止。最初伪代码层次过高,不断的精化和分解伪代码,直到再写伪代码实在浪费时间为止
* 编写子程序代码 Code the Routine
* 图9.3 at p225
* 写出子程序的声明:把若是接口名称起得直接了当,就不须要接口假定(interface assumption)的事情
* 把伪代码转变为高层次的注释
* 在每条注释下填充代码:
伪代码至关于文章的提纲,每段伪代码注释描述类一段或者一句代码
* 检查代码是否须要进一步分解
* 若是一行伪代码下的代码过多,能够refactor重构成一个子程序
* 递归recursively地应用伪代码编程过程。若是一行伪代码下的代码过
多,能够把一行伪代码拆分为多行伪代码
- 检查代码 Check the Code
- 在脑海检查程序的错误:当子程序足够短小精悍,检查到程序全部可能的执行路径、端点和异常状况,不但要本身查(这叫桌面检查 desk checking),能够同行审查peer review,详查walk-through或者审查inspection
从superstition迷信到理解,调查显示只有5%的错误是因为编译器或者硬件形成的,因此遇到的错误大部分都是程序员自身形成的
* 编译子程序:把编译器的警告级别调到最高,使用像lint的检查工具,及时消除产生错误信息和警告的全部根源
* 在调试器中逐行运行代码
* 测试代码
* 消除程序中的错误
- 收尾工做 Clean Up LeftOvers
- 检查子程序的接口
- 检查子总体的设计质量:内聚性;子程序间松散耦合;防护式编程C7
- 检查子程序的变量C10-13
- 检查子程序的语句和逻辑:有无泄漏资源,错误,死循环,错误嵌套C14-19
- 检查子程序的布局:格式化 C31
- 检查子程序的文档
- 除去冗余的注释
- 9.4 伪代码编程过程的替代方案 Alternative to the PPP
- 测试先行开发(测试驱动开发)Test-first Development:在任何代码以前先要写出测试用例,使得程序可测试
- 重构refactoring C24
- 契约式设计 design by contract 即每一段程序都具备前条件preconditions和后条件postconditions
- 东拼西凑hacking
- 核对表 P233
第三部分 变量 Variable
- 第十章 使用变量的通常事项 General Issue in Using Variables
- 10.1 数据认知: 列举的常见的数据类型
- 10.2 轻松掌握变量定义:
- 隐式声明:避免隐式声明,可能致使编译器的初始值不符合编程的要求,应该关闭隐式声明,并声明所有变量;
- 10.3 变量初始化原则:
- 初始化错误:
- 从未对变量赋值
- 变量值已通过期
- 变量的一部分被赋值
- 避免初始化方法:
- 在声明时初始化变量
- 在靠近变量第一次使用的地方初始化它
- ****理想状态下****,在第一次使用的地方声明并初始化变量
- 在可能的状况下,使用final或者const
- 在计数累加器i j,再次使用时忘记重置是一个常见的错误
- 在类的构造函数中初始化该类数据成员
- 检查变量是否须要从新初始化
- 一次性初始化具名常量,用可执行代码初始化变量。
- 10.4 做用域 Scope
- 使变量引用局部化:跨度span:变量引用点之间的距离;应该把变量的引用点集中起来
、 * 尽量缩短存活时间:就是变量最初引用点到最后引用点之间的距离
- 减小做用域的通常原则:
- 在循环开始前初始化该循环里使用的变量,而不是在该循环所属的子程序开始处初始化这些变量
- 直到变量即将被使用的时候再为其赋值
- 把相关的语句放在一块儿
- 把相关的语句提出成单独的子程序
- 开始时采用严格的做用域,而后根据须要扩展变量的做用域
> private -> protected -> default- >public
- 10.5 持续性:有可能数据发生了变化,引用了过时的变量致使错误
- 在子程序加入调试代码或者断言检查关键数据的合理性
- 准备抛弃变量时给它设置不合理的值 我的认为在at C++
- 编写程序假定是没有持续性的,但不适合c++或者java中的static数据
- 养成使用全部数据前声明和初始化的习惯
- 10.6 绑定时间:绑定时间越早灵活性越差,其实跟上面的初始化变量的指导是同样的
- 10.7 数据类型和控制结构之间的关系:
- 序列型数据翻译为程序中的顺序语句
- 选择型数据翻译为程序中的if else语句
- 迭代型数据翻译为for repeat while等循环语句
- 10.8 为变量指定单一用途
- 第十一章 变量名的力量
- 11.1 选择好变量名的注意事项
- 最终重要的命名事项:
- 名字要彻底、准确地表达该事物
- 容易阅读、不包含晦涩的缩写、同时无歧义
- 以问题为导向:好的名字是表达”什么“(what)而不是”如何“(how),即反映问题而不是解决方案。变量应直指问题的领域而非计算机世界
inputRecord 比 employeeData
* 最适当的名字长度 Optimum Name Length:
* 有研究是平均长度在10到16个字符
* 有研究是平均长度在8到20个字符
* 最重要是强调当本身代码中出现不少更短的名字,认真检查确保名字含义足够清晰
* 变量名对做用域的影响 The Effect of Scope on Variable Names、
* 对位于全局命名空间中的名字加入限定词
* 若是编程语言不支持命名空间,就在对应的子系统加入前缀
- 变量名中计算值限定空间 Computed-Value Qualifiers In Variable Names
- 把总额 sum total、平均数 average 、最大值Max、最小值Min、记录Record、字符串String、Pointer指针加入名字的后面
revenueTotal revenueAverage更具对称性,更容易维护
Num放在开始位置表明总数,放在结束位置表明一个下标
customerCount表明员工总数 customerIndex表明某个特定的员工
- 变量名中的对仗词 Common Opposites In Variable Names
- locked/unlocked
- min/max
- next/previous
- old/new
- opened/closed
- visible/invisible
- source/target
- source/destination
- up/down
- 11.2 为特定类型的数据命名 Naming Specific Types of Data
- 为循环下标命名 Naming Loop Index:
- 通常状况下使用i、j、k
- 多层循环嵌套的状况或者循环长度超过一两行代码,应该给计数器赋予更长的名字以表达其含义
- 为状态变量命名 Naming Status Variables
- 为状态变量取一个比flag更好的名字
- 标记应该用枚举类型、具名常量或者做为具名常量的全局变量对其进行赋值,增长可读性
- 为临时变量命名 Naming Temporary Variables:在使用temp等命名前最好思考是否有能表达其含义的名字
- 为布尔变量命名 Naming Boolean Variables:
- 谨记典型的布尔变量命名:
- done 表示事情是否完成,未完成前是false,完成后是true
- error 表示有错误发生,错误发生前是false,错误发生后是true
- found 表示某个值已经找到,在未找到该值前是false,找到该值后是true
- success或者ok 表示一项操做是否成功,失败是false,成功是true
- 为布尔变量赋予隐含“真/假”含义的名字
- status不是一个好的命名
- isXXX优势是:不能用于哪些模糊的名字。缺点是可读性较差
isStatus无心义 isFound 比found可读性差
* 使用确定布尔命名
- 为枚举变量命名 Naming Enumerated Types:
- 能够经过使用组前缀为明确表示该类型的成员都同属于一个组
Color_XXX
* 对于枚举的命名有不一样的观点:有大小写混合Color_Blue
;与常量相似的大写Color.BULE
* 在处理枚举像类的编程语言里,处理枚举很像类,因此枚举成员老是冠以枚举名字前缀,就无需重复前缀了
- 为常量命名 Naming Constans:应命名该常量表明的抽象事物而非数值
- 11.3 命名规则的力量 The Power of Naming Covenstions
- 为何要有规则
- 要求你更多按规矩办事,集中精力投入关注代码更重要的特征
- 有助于项目之间传递知识
- 有助于学习新项目
- 有助于减小名字增生 name Proliferation
- 弥补编程语言的不足
- 强调相关变量之间的关系:把相关变量设置相同前缀将它们关联起来
- 什么时候采用命名规则
- 多人开发
- 程序须要转交别人
- 程序规模太大,没法同时了解全局,必需分而治之
- 程序开发周期过长
- 一个项目存在一些不常见的术语,在编写代码中使用术语或者缩写的时候
- 11.4 非正式命名规则 Informal Naming Conventions
- 与语言无关的命名规则指导原则
- 与语言相关的命名规则指导原则:有C,C++,只列举Java
- i,j是整数下标
- 常量所有大写并用下划线分割
- 类名和接口每一个单词首字母大写
- 变量名和方法名第一个单词首字母小写,后续单词首字母大写
- 除用于所有大写的名字外,不使用下划线做为名字中的分隔符
- 访问器子程序使用get和set前缀
- 混合语言编程注意事项
- 命名规则示例
- 包含如下三类信息:
- 变量的内容(是什么)
- 数据的种类(具名常量,简单变量,用户自定义类型或者类)
- 变量的做用域(局部,私用的,类的,包的或者所有的做用域)
- 类成员数据:mXXX,全局变量:gXXX
- 11.5 标准前缀 Standardized Prefixes:分用户自定义类型(UDT)的缩写和语义前缀
- 用户类型缩写 User-Defined Type Abbreviation:UDT缩写能够标识被命名对象或者变量的数据类型
- 语义前缀 Semantic Prefixes:描述变量或者对象是如何被使用的
- c:count 数量
- first:数组须要处理的第一个元素
- g:全局变量
- i:数组的下标
- last:数组中须要处理的最后一个元素
- lim:数组中须要处理的元素上限,是非法的,不存在的上限,而last是合法的
- m:类一级的变量
- min:数组或者其余种类列表中绝对最前一个元素
- max:数组或者其余种类列表中绝对最后一个元素
- p:pointer 指针
- 标准前缀的优势:使名字更紧凑、增长可读性
- 11.6 建立具有可读性的短名字 Creating Short Names That Are Readable
- 缩写的通常指导原则:
- 使用标准缩写(参考词典)
- 去掉全部非前置元音???
computer->cmptr screen->scrn
* 去掉虚词
> and、or、the等
* 使用每一个单词的第一个或者前几个字母
* 统一地使用单词的第1、第二或者第三(自行肯定)字母后截断
* 保留每一个单词的第一和最后一个字母
* 使用名字中每一个重要单词,最多不超过3个
* 去除无用的后缀
> ed,ing
* 确保不要改变变量的含义
- 11.7 应该避免的名字 Kind of Names To Avoid
- 避免使用使人误解的名字或者缩写
- 避免使用具备类似含义的名字
- 避免使用具备不一样含义可是类似名字的变量:
通常是缩写很类似的状况如 clientReq 和 clientRes
* 避免使用发音类似的名字
* 避免在名字中使用数字
* 避免拼错单词
* 避免使用容易拼错的单词
* 不要仅靠大小写来区分变量名
* 避免使用多种天然语言:只有英语,不使用汉语等
* 避免使用标准类型、变量和子程序名字:编程语言的关键词
* 不要使用与变量含义无关的名字
* 避免在名字中使用容易混淆的字符:数字1对于字母l或者字母i,数字2对应字母z,数字5对应字母s,数字6对应字母g,数字0对应字母o
- 第十二章 基本数据类型
- 12.1 数值概论
- 避免神秘数值
- 可使用硬编码的1或者0
- 预防除零错误
- 使类型转换变得明显:不一样的数据类型之间会发生转换时,利用显式转换而非隐式转换
- 避免混合类型的比较:应该转换成相同类型再进行比较
- 注意编译器警告
- 12.2 整数 Integers
- 检查整数除法
- 检查整数溢出:在整数加法或者乘法的过程当中,留心较大的整数。
- 12.3 浮点数 Floating-Point Numbers
- 避免数量级相差巨大之间加减运算:解决方案:对于一系列相差巨大的数进行运算,先进行从小到大排序,从最小值开始把它们加起来,并不能消除舍入问题,可是能减小到最低限度。
1000 000.00+0.1可能等于1000 000.00
* 避免等量判断:肯定数值在可接受的精度范围内
> double类型变量,for循环中 加0.1,十次后不必定等于1.0
* 处理舍入偏差问题:
* 换用精度更高的变量类型
* 把浮点变量变成整数变量
* 检查语言和函数库对特定数据类型的支持
* 12.4 字符和字符串 Characters and Strings
* 避免神秘字符和神秘字符串
* 了解你的语言和开发环境是如何支持Unicode
* 在程序生命周期中尽早决定国际化/本地化策略
* 只支持一种文字语言,考虑使用ISO-8859字符集
* 须要支持多语言,请使用unicode
* 使用某种一致的字符串转换策略
* 12.5 布尔变量
* 用布尔变量对程序加以文档说明:对于表达式的结果赋予一个布尔变量,以提升可读性
* 用布尔变量简化复杂判断:这是在上一条意见演化出来的,也是提升可读性
* 若是须要的话,建立你本身的布尔类型
* 12.6 枚举类型 Enumerated Types
* 用枚举提升代码可读性
* 用枚举提升代码可靠性
* 用枚举是程序易于简化修改
* 使用枚举做为布尔值的替代方案:有多种失败的类型
* 检查非法数值
* 定义枚举的第一项和最后一项以用于循环边界
> Enum Country{
Country_First = 0;
Country_China = 0;
Country_USA = 1;
Country_Last = 1;
}
* 把枚举的第一个元素留做非法值
> Enum Country{
Country_InvalidFirst = 0;
Country_First = 1;
Country_China = 1;
Country_USA = 2;
Country_Last = 2;
}
* 明肯定义项目代码编写标准中第一个和最后一个元素的使用规则
* 警戒给枚举元素明确赋值而带来错误:
> Enum Country{
Country_InvalidFirst = 0;
Country_First = 2;
Country_China = 2;
Country_UK = 4;
Country_USA = 6;
Country_Last = 6;
}
遍历的时候会遍历到1,3,5这些非法值
* 若是你的语言没有枚举类型:p307
* 12.7 具名常量 Named Constants
* 12.8 数组 Arrays
* 确保数组下标没有越界
* 考虑用容器取代数组,或者将数组做为顺序化结构来处理
* 检查数组的边界
* 数组是多维,保证下标的使用顺序正确,防止下标串话
* 在C中结合ARRAY_LENGTH()宏来使用数组
* 12.9 创造本身的类型(类型别名)Creating Your Own Types(Type Aliasing)
- 第十三章 不常见的数据类型
- 13.1 结构体 Structure:指使用其余类型建立的数据,相似java中没有公用子程序,彻底由公用数据成员组成的类,我的认为就是封装
- 用结构体明确数据关系:归为一类,关联起来
- 用结构体简化对数据块的操做
- 用结构体简化参数列表
- 用结构体减小维护
- 13.2 指针 Pointers???未学,略
- 用来理解指针的范例:指针:内存中的某个位置+如何解释该位置的内容
- 内存中的位置:就是一个地址,以16进制数表示
- 如何解释指针所指的内容:由指针的基类型 base type决定
- 使用指针的通常技巧:略
- 13.3 全局数据 Global Data
- 与全局变量有关的常见问题:
- 无心间修改了全局数据
- 与全局数据有关的奇异的和使人激动的别名问题:就是出现两个或者以上的名字都是指同一个变量
- 与全局数据有关的代码重入(re-entrant)问题:多线程状况下全局数据不只是不一样子程序共享,同时同一程序的不一样拷贝之间也共享
- 全局数据阻碍代码重用
子程序用到全局数据,不能直接将子程序复制到其余地方(其余类)里面,解决方法:上策是修改旧类将全局数据局部化;下策是在新类建立与旧类相同的全局数据,致使像病毒同样传染
* 与全局数据有关的非肯定的初始化顺序事宜
> 在初始化一个类的变量时须要使用其余文件的初始化全局变量,因此须要采用明确手段保证两个变量按照正确顺序进行,否则将致使错误
* 全局数据破坏了模块化和智力上的可管理性
* 使用全局数据的理由:
* 保存全局数据:好比程序是否debug等
* 模拟具名常量
* 模拟枚举类型
* 简化对极其经常使用数据的使用
* 消除流浪数据(tramp data):
有时候传递数据给一个子程序或者类,只是想传递给另外一个子程序或者类,若是调用链中间的子程序并不适用这一对象的时候,就称这些数据为流浪数据
* 只有万不得已才使用全局数据
* 按照"局部数据->private数据->protected数据->全局数据"顺序设置数据的做用域
* 区分全局变量和类变量
* 使用访问器子程序
* 用访问器子程序来取代全局数据
* 访问器子程序的优点
* 得到对数据的集中控制:若是要修改结构方法是须要修改子程序便可
* 确保变量的全部引用获得保护,避免出现异常
* 访问器子程序能够容易转变为抽象数据类型:即经过子程序名称实现抽象,提升代码可读性
* 如何使用访问器子程序
* 要求全部数据经过子程序访问
* 不要把全局数据放在一块儿,而是放在相应抽象水平的类里面
* 用锁来控制对全局数据的访问:在多线程下,子程序访问器加锁,保证数据正确性
* 使得对一项数据的全部访问都发生在同一抽象层上
> 若是有add(event),就会有remove(event)
* 如何下降使用全局数据的风险
* 建立一种命名规则来突出全局变量
> gXXX
* 为全局变量建立一份注释良好的清单
* 不要用全局变量存储中间结果
* 不要把全局变量都放在一个大对象中并处处传递,以说明你没有使用全局变量
> 全局变量应根据其抽象层次防到相应的类中
第三部分 语句 statement
- 第十四章 组织直线型代码 Organizing Straight-Line Code
- 14.1 必须有明确顺序的语句 statements That Must be in Specific Order
-
设法组织代码,让依赖关系变得很是明显
-
使子程序名能突显依赖关系
-
利用子程序参数明确显示依赖关系
参数
init(expenseData);
dayExpensse(expenseData);
monthlyExpensse(expenseData);
anunalExpensse(expenseData);
带返回值
expenseData = init(expenseData);
expenseData = dayExpensse(expenseData);
expenseData = monthlyExpensse(expenseData);
expenseData = anunalExpensse(expenseData);
用数据代表依赖关系不重要
init(expenseData);
dayExpenseData = dayExpensse(expenseData);
monthlyExpenseData = monthlyExpensse(expenseData);
anunalExpensseData = anunalExpensse(dayExpenseData ,monthlyExpenseData );
* 用注释对不清晰的依赖关系进行说明
* 用断言或者错误处理代码来检查依赖关系:可是增长类复杂度,采用的时候须要衡量利弊
- 14.2 顺序无关的 语句 Statements Whose Order Don't Matter
- 使代码易于自上而下地阅读 Making Code Read From Top to Bottom:跟把相关代码组织在一块儿时道理是同样的
- 把相关代码组织在一块儿 Grouping Related Statements
- 第十五章 使用条件语句 Using Conditionals
- 15.1 if语句
- 简单的if-then语句
- 首先写正确代码路径,再处理不常见状况
- 确保等量分支是正确的:不要漏掉特定状况
- 把正常状况的处理放在if后面而不要放在else后面
- if后面不要跟空语句:要不就改为 if(!XXX){};
- 考虑else语句:若是须要能够配个空的else语句并加以说明
- 测试else语句的正确性
- 检查if else语句是否是弄反
- if-then-else语句串 Chains of if-then-else statements:
- 使用布尔值调用简化复杂的检测
- 把最正确的状况放在最前面
if(xxx){
}else if(xxx){
}
else if(xxx){
}
* 若是语言支持把if-then-else语句串替换成其余结构:case语句,更清晰
- 15.2 case语句 case Statements
- 为case语句选择最有效的排序
- 按字母顺序或者数字顺序排列各类状况:全部状况的重要性相同
- 把正常的状况放在前面
- 按执行频率排列case语句
- 使用case语句的诀窍
- 简化每种状况对应的操做:对于某种状况的操做过于复杂,应该变成一个子程序
- 不要为了使用case语句而刻意制造一个变量
- 把default语句只用于检查真正默认的状况
还剩一个状况,用default去检查是不对的
* 使用case穿越(穿透)须要注释说明状况
* 核对表at p635
- 第十六章 控制循环
- 16.1 选择循环的种类 Selecting the Kind of Loop p367
- 种类:
- 计数循环 counted loop
- 连续求值循环 continuously evaluated loop
- 无限循环 endless loop
- 迭代器循环 iterator loop
对于C、C++、Java:for、foreach 、while、检查位置都是开始 do- while是结尾
灵活度除了foreach 是严格以外其余均是灵活
do-while至少执行一次,其余能够不执行
* 何时使用while循环 When to Use a Loop-While-Exit Loop
* 何时用带退出的循环
* 正常带退出的循环
* 带退出的循环更容易理解
* 带退出的循环可能使退出的地方不少,可能致使在调试、修改或者测试时被忽略,若是可能尽量把退出的代码写在一个地方
* 非正常带退出的循环
* 何时使用for循环 When to Use a for Loop:
你在循环头处写好后即把它忘掉,无须再循环中作任何事情去控制它,若是有一个必须使循环从循环退出的条件,就使用while循环
* 何时使用foreach循环 When to Use a foreach Loop:消除循环内务处理算数,防止off-by-one越界错误
- 16.2 循环控制 Controlling the Loop p373
- 防止出现错误的方法:
- 减小能影响该循环各类缘由的因素:说了跟没有同样——
- 把循环内部当作子程序,把控制尽量放在循环体外
while(XXX && XXX && (XXX||XXX)){
XXXXXXXXXX
}
- 进入循环 Entering Loop
- 只从一个位置进入循环
- 把初始化代码紧放在循环前面
- 用while(true)表明无限循环
- 在适当状况下多使用for循环
由于for循环把循环控制代码集中了,while须要在循环顶部初始化循环条件,而后在底部修改循环的相关代码
* 在while循环更适用的时候,不要使用for循环
> 不是for(xx;xx;xx)中间代码不是简单对计数值进行判断,而是其余表达式则应当该为while循环
* 处理好循环体 Processing The Middle of the Loop
* 用{}
将循环体重的语句包起来:我的:即便是一条语句也须要,由于扩展、修改程序可能会出现意料以外的错误
* 避免空循环:不要出现循环体为空的状况,应改为do-while
* 把循环体的内务操做要么放在循环开始处,要么放在结尾处:内务操做如i= i+1这样的表达式
* 一个循环只作一件事
* 退出循环 Exiting Loop
* 设法确认循环可以终止:考虑正常状况、端点以及每一种异常状况
* 不要为了终止循环混乱修改for循环下标
* 避免出现依赖于下标最终取值的代码:下标值只在循环体内有效,能够说是缩小做用域的一种作法
* 考虑使用安全计数器
* 提早退出循环
* 考虑在while循环中使用break而不是布尔标记:
就是循环体中先后操做有条件限制关系,当达到某个条件,不执行后续操做时,应当使用break直接退出
* 当心那些有不少break散布在循环中
* 在循环开始处使用continue:提升可读性
* 若是语言支持,请使用带标记号break结构:是break退出的目标一目标然
* 使用break和continue要当心谨慎
* 检查端点 Checking EndPoints
> 简单的循环:开始状况+任意选择的中间状况+最终状况,先脑海模拟,若是有复杂计算,手动检查计算是否正确
* 使用循环变量 Using Loop Variables
* 用整数或者枚举类型表示数组和循环的边界
* 嵌套循环中使用有意义的变量名提升其可读性
* 用有意义的名字防止循环下标串话
* 把循环下标变量限制在本循环内
* 循环应该多长 How Long Should a Loop Be
* 把循环代码的行数限制在50行之内
* 把嵌套限制在3层之内
* 把长循环的内容移到子程序内
* 要让长循环格外清晰
- 16.3 轻松建立循环-由内而外 Creating Loop Easily- From the inside Out p385
- 16.4 循环和数组的关系 Corresponse Between Loop And Arrays p387
- 第十七章 不常见的控制结构 Unusual Control Structures
- 17.1 子程序中多处返回 Multiple Return From a Routine p391
- 若是能增长可读性,就用return
- 用防卫句子(guard clause)(早返回或早退出) 来简化复杂的错误处理
- 17.2 递归 Recursion p393
- 使用递归的技巧
- 确认递归可以终止
- 使用安全计数器防止无限循环
- 把递归限制在一个子程序里面
- 留心栈空间:防止栈溢出
- 不要用递归去计算阶乘和斐波那契数列
- 17.3 goto p398
- 反对goto的观点 The Arguments Against gotos
- 支持goto的观点 The Arguments for gotos
- 关于goto的虚假辩论 The phony goto Debate
- 错误处理和goto Erro Processing And goto
- goto和在else字句中的共享代码 goto And The Sharing Code In an else Clause
- goto使用原则总结 Summary of Guidlines For Using *gotos *
- 17.4 针对不常见控制结构的观点 Perspective on Unusual Control Structures
- 第十八章 表驱动法 Table-Driven Methods
- 第十九章 通常控制问题 General Control Issue
- 19.1 布尔表达式 Boolean Expressions p431
- 用true或者false作布尔判断 Using true of false For Boolean Tests
- 用true或者false作判断,而不用0和1等数值作判断
- 隐式地比较布尔值与true和false:
while(success){xx}
而不是while(success == true){xxx}
* 简化复杂的表达式 Making Complicated Expression Simple
* 拆分复杂判断并引入新的布尔变量
* 把复杂的表达式作成布尔函数
* 用决策表替代复杂的条件
* 编写确定形式的布尔表达式 Forming Boolean Expression Positively
* 先确定再否认语句
> if(xx){xxx}else{xxx}
而不是if(!xx){xxx}else{xxx}
* 用狄摩根定理(逆反定理)简化否认的布尔判断
* not A and not B = not(A or B)
not A or not B = not (A and B)
* 用括号使布尔表达式更清晰 Using Parentheses to Clarify Boolean Expressions
* 用简单的技术技巧来使括号对称:开始为0,遇到一个左括号加1,遇到一个右括号减1
* 把布尔值括在括号里面
* 理解布尔表达式如何求值 Knowing How Boolean Expression Are Evaluated
* 按照数轴顺序编写数值表达式 Writing Numeric Expressions in Number-Line Order
* 从左到右,从大到小
* 与0比较的知道原则 Guidelines for Comparisons to 0
* 隐式地比较逻辑变量
* 把数和()相比较
* 在C中显式地比较字符和零终止符(‘\0’)
* 把指针与NULL相比较
* 布尔表达式的常见问题
* java中 == 和equal的区别
- 19.2 复合语句(语句块)Compound Statements (Blocks) p443
*把括号对一块儿写出
- 19.3 空语句 Null Statements p444
- 当心使用空语句
- 为空语句建立一个DoNothing()预处理函数或者内联函数:强调空语句
- 考虑若是换用一个非空的循环体,是否会让代码更清晰
- 19.4 驯服危险的深层嵌套 Taming Dangerously Deep Nesting p445
- 经过重复检测条件中某一部分来简化嵌套的if语句
- 用break来简化嵌套if
- 把嵌套if转换成一组if-then-else语句:即转换成
if(xxx){xxx}elseif(xxx){xxx}elseif(xxx){xxxx}
- 把嵌套if转换成case语句
- 把深层嵌套的代码抽取出来放进单独的子程序
- 使用一种更面向对象的方法:即多态
- 从新设计深层嵌套代码
- 争议 用状态变量重写代码(增长复杂度)17.3
- 争议 用防卫字句退出子程序,从而使代码的主要路径更为清晰 17.1
- 使用异常 8.4
- 19.5 编程基础:结构化编程 p454
- 核心思想:应用程序只采用一些单入口,单出口的控制结构。单入单出的控制结构就是一个代码块,只能从一个位置开始执行,而且只能结束于某个位置。(有且只有一个入口和出口)
- 结构化编程的三个组成部分 The Three Components of Structured Programming
- 顺序 Sequence:赋值和调用子程序
- 选择 Selection:if和case语句
- 迭代 Iteration:
- 19.6 控制结构与复杂度 Control Structure and Complexity p456
- 复杂度的重要性 How Import is Complexity
- 下降复杂度的通常性原则 General Guidelines For Reducing Complexity
程序一开始为1
一旦遇到关键词或者其同类加1:if repeat,while,for,and,or
给case语句中每一种状况加1
* 如何处理复杂度的度量结果
> 0-5子程序可能不错|
6-10得想办法 简化子程序类
10+把子程序 的某一部分拆分红另外一个子程序并调用它
第五部分 代码改善 Code Improvement
- 第二十章 软件质量描述 p463
- 20.1 软件质量的特性
- 外在特性:
- 正确性 Correctness 指系统规范、设计和实现方面的错误的稀少程度
- 可用性 Usability:指用户学习和使用的成本
- 效率 Efficiency:指占用的内存,储存和执行时间
- 可靠性 Reliability:在指定必需条件下,完成所需功能的能力:很长的无端障时间
- 健壮性 Robustness:接受无效数据或者在压力环境下运行的能力
- 完整性 Integrity:阻止对程序或者数据进行未经验证或者不正确访问的能力
- 适应性 Adaptability:为特定应用或者环境参数设计的系统,能不修改的状况给其余应用或者环境使用
- 精确性 :输出结果的偏差程度
- 内在特性:
- 灵活性 Flexibility: 适应需求
- 可维护性 Maintainability
- 可移植性 Portability
- 可重用性 ReuSability
- 可读性 Readability
- 可测试性 Testability
- 可理解性 Understandability
- 20.2 改善软件质量的技术
- 20.3 不一样质量保障技术的相对效能
- 20.4 何时进行质量保证工做
- 20.5 软件质量的广泛原理
- 第二十一章 协同构建 p479
- 第二十二章 开发者测试 p499
- 22.1 开发者测试在软件质量中的角色 p500
- 测试的种类:
- 单元测试(Unit testing):测试的代码:一个程序员或者一个团队编写 代码规模:完整的类、子程序或者小程序
- 组件测试(Component testing)测试代码:一个类、包小程序或者其余程序元素 代码规模:涉及到多个程序员或者多个团队
- 集成测试(Integration testing):是对两个或更多的类、包、组件或者子系统进行联合测试。这些组件由多个程序员或者开发团队所建立。
- 测试的做用:
- 测试与其余开发活动背道而驰,测试的目标识找出错误,成功的测试是弄垮软件,而其余开发活动是避免程序错误和软件崩溃
- 测试永远不可能完全证实程序中没有错误
- 测试并不能解决错误,改善软件质量
- 程序员倾向于作出”干净“的测试
- 构建中测试
- 构建期间:先根据需求构想子程序->编写子程序或者类->先在脑海检查->进行复查或者测试
- 每写一个子程序,对其进行独立测试,确实不容易,可是单独调试比继承以后再测试容易多。
- 新加入的子程序发现新的错误,就知道这是子程序或者其接口引起的问题
- 22.2 开发者测试的推荐方法 Recommend Approad to Developer Testing p503
- 基础方法:
- 对每一项相关的需求进行测试
- 对每隔相关的设计关注点进行测试
- 用基础测试basic testing来扩充针对需求和设计的详细测试用例:增长数据流测试 data-flow test,而后补充其余所需的测试用例
- 使用一个检查表,其中记录着你在项目中所犯的错误
- 测试先行仍是测试后行:测试先行
- 先写测试用例只是工做顺序问题:所花代价同样
- 先编写测试用例,能够更早发现缺陷
- 开发者测试的局限性
- 开发者测试倾向于”干净“测试
- 开发者测试对于代码覆盖率过于乐观
- 开发者测试每每忽略一些更复杂的测试覆盖率类型
- 22.3 测试技巧锦囊 p505
- 不完整的测试Incomplete Testing:进行完整测试是不可能的事情
- 结构化的基础测试 Structured Basic Testing
- 测试程序中的每一条语句至少一次
- 结构化基础测试最少数量计算:
- 对于经过子程序的直路,开始的时候加1
- 遇到每一个关键词或者等价物加1,如:if、while、repeat、for、and以及or
- 遇到每一个case语句就加1
- 数据流测试 Data-Flow Testing:数据流出错率不低于控制流
- 数据的状态
- 已定义:数据已经初始化了,可是没有被使用
- 已使用:数据已经用于计算或者做为某子程序的参数
- 已销毁:变量出了做用域或者指针已经被释放
- 数据的状态的组合:正确的是已定义-已使用
- 等价类划分 Equipment Partitioning
- 猜想错误 Error Guessing
- 边界值分析 Boundary Analysis
- 几类坏数据 Classes of Bad Data
- 数据太少(没有数据)
- 太多的数据
- 错误(无效)的数据
- 未初始化的数据
- 几类好数据 Classes of Good Data
- 正常的状况-中间或者指望值
- 最小的正常局面
- 最大的正常局面
- 与旧数据的兼容性
- 22.4 典型错误 p517
- 错误不是均匀分布的
- 绝大数错误与少数几个具备严重缺陷的子程序有关
- 错误有几大类:结构方面的错误?,数据方面的错误,已实现的功能(多是说随着迭代,新需求跟旧实现之间的错误)
- 大多数错误的影响范围有限
- 大多数构建错误是编程人员的错误形成的
- 拼写错误是一个常见的问题
- 22.5 测试支持工具 Test-Support Tools p523
- 为测试各个类构造脚手架 Building Scaffolding to Test Individual Classes
- 哑类(模仿对象/桩对象) dummy class(mock object/stub object):根据所需的真实性决定他们与现实的近似程度
- 调用待测试的真实函数的伪造函数,称为“驱动函数”或者“测试夹具”
- 测试数据生成器 Test-Data Generations
- 产生程序员意想不到的数据组合
- 比起手工构造测试数据,随机数据生成器可以更完全地进行测试
- 覆盖率监视器 Coverage Monitors
- 数据记录器/日志记录器
- 符号调试器
- 系统干扰器
- 错误数据库
- 22.6 改善测试过程 Improving Your Testing p528
- 有计划的测试
- 从新测试(回归测试):在完全检查代码的前提下,当相关代码发生改变以后,须要从新测试,可能产品迭代增长新的测试用例的同时应保留旧的测试用例
- 自动化测试
- 22.7 保留测试记录 Keeping Test Records p529
做者:白桦叶 连接:https://www.jianshu.com/p/7b7228ebba55 來源:简书 著做权归做者全部。商业转载请联系做者得到受权,非商业转载请注明出处。