代码大全2
2017-07-01 14:09:04 45 举报
AI智能生成
代码大全2读书笔记, 包括各级目录及要点,核对表。
作者其他创作
大纲/内容
第五部分 代码改善
第 20 章 软件质量概述
20.1 软件质量的特性
外在的质量特性
正确性
可用性
效率
可靠性
完整性
适应性
精确性
健壮性
内在的质量特性
可维护性
灵活性
可移植性
可重用性
可读性
可测试性
可理解性
20.2 改善软件质量的技术
软件质量中的某些要素
软件质量目标
明确定义质量保证工作
测试策略
软件工程指南
非正式技术复查
正式技术复查
外部审查
开发过程
对变更进行控制的过程
结果的量化
制作原型
设置目标
20.3 不同质量保障技术的相对效能
缺陷检测率
找出缺陷的成本
修正缺陷的成本
20.4 什么时候进行质量保证工作
20.5 软件质量的普遍原理
改善质量以降低开发成本
核对表
要点
第 21 章 协同构建
21.1 协同开发实践概要
协同构建是其他质量保证技术的补充
协同构建有利于传授公司文化以及编程专业知识
集体所有权适用于所有形式的协同构建
在构建前后都应保持协作
21.2 结对编程
成功运用结对编程的关键
用编码规范来支持结对编程
不要让结对编程编程旁观
不要强迫在简单的问题上使用结对编程
有规律地对结对人员和分配的工作进行轮换
鼓励双方跟上对方的步伐
确认两个人都能够看到显示器
不要强迫程序员与自己关系紧张的人组队
避免新手组合
指定一个组长
结对编程的好处
核对表
21.3 正式检查
你期望详查能够带来什么结果
详查中的人员角色
主持人
作者
评论员
记录员
经理
详查的一般步骤
计划
概述
准备
详查会议
详查报告
返工
跟进
第三个小时的会议
对详查进行细调
详查中的自尊心
详查和《代码大全》
详查总结
核对表
21.4 其他类型的协同开发实践
走查
你期望走查能够得到什么样的结果
代码的阅读
公开演示
协同构建技术的比较
要点
第 22 章 开发者测试
常见测试方法
单元测试
组建测试
集成测试
回归测试
系统测试
22.1 开发者测试在软件质量中的角色
构建中测试
22.2 开发者测试的推荐方法
测试先行还是测试后行
开发者测试的局限性
开发者测试倾向于“干净测试”
开发者测试对覆盖率有过于乐观的估计
开发者测试往往会忽略一些更复杂的测试覆盖率类型
22.3 测试技巧锦囊
不完整的测试
结构化的基础测试
数据流测试
数据的常见状态
已定义
已使用
已销毁
已进入
已退出
数据状态的组合
等阶类划分
猜测错误
边界值分析
复合边界值
几类坏数据
几类好数据
采用容易手工检查的测试用例
22.4 典型错误
哪些类包含最多的错误
错误的分类
大多数错误的影响范围是相当有限的
许多错误发生在构建的范畴之外
大多数的构建错误是编程人员的失误造成的
让人惊奇的是,笔误(拼写错误)是一个常见的问题根源
研究程序员所犯错误原因时,错误理解设计这条会经常出现
大多数错误都很容易修正
总结所在组织对付错误的经验
不完善的构建过程引发错误所占的比例
你期望能发现多少错误
测试本身的错误
22.5 测试支持工具
为测试各个类构造脚手架
Diff 工具
测试数据生成器
覆盖率监视器
数据记录器/日志记录器
符号调试器
系统干扰器
错误数据库
22.6 改善测试过程
有计划的测试
重新测试(回归测试)
自动化测试
22.7 保留测试记录
个人测试记录
核对表
要点
第 23 章 调试
23.1 调试概述
调试在软件中所扮演的角色
调试效率的巨大差异
让你有所收获的缺陷
理解你正在编写的程序
明确你犯了哪种类型的错误
从代码阅读者的角度分析代码质量
审视自己解决问题的方法
审视自己修正缺陷的方法
一种效率低下的调试方法
调试之魔鬼指南
凭猜测找出缺陷
不要把时间浪费在理解问题上
用最唾手可得的方式修正错误
迷信式调试
23.2 寻找缺陷
科学的调试方法
把错误的发生稳定下来
确定错误原因
寻找缺陷的一些小建议
在构造假设时考虑所有的可用数据
提炼产生错误的测试用例
在自己的单元测试族(unit test suite)中测试代码
利用可用的工具
采用多种不同的方法重现错误
用更多的数据生成更多的假设
利用否定性测试用例的结果
对可能的假设尝试头脑风暴
在桌上放一个记事本,把需要尝试的事情逐条列出
缩小嫌疑代码的范围
对之前出现过缺陷的类和子程序保持警惕
检查最近修改过的代码
扩展嫌疑代码的范围
增量式集成
检查常见缺陷
同其他人讨论问题
抛开问题,休息一下
蛮力调试
在使用“快速肮脏调试法”的时候设置一个时间上限
做出一张蛮力调试方法列表
语法错误
不要过分信任编译器信息中的行号
不要迷信编译器信息
不要轻信编译器的第二条信息
分而治之
找出没有配对的注释或行号
23.3 修正缺陷
在动手之前先要理解问题
理解程序本身,而不仅仅是问题
验证对错误的分析
放松一下
保存最初的源代码
治本,而不是治标
修改代码时一定要有恰当的理由
一次只做一个改动
检查自己的改动
增加能暴露问题的单元测试
搜索类似的缺陷
23.4 调试中的心理因素
心理取向如何导致调试时的盲目
“心理距离”在调试中的作用
23.5 调试工具---明显的和不那么明显的
源代码比较工具
编译器的警告消息
将编译器的警告级别设置为最高级,尽可能不放过任何一个警告,然后修正编译器所报告的全部错误
用对待错误的态度来处理警告
在项目组范围内使用统一的编译设置
增强的语法检查和逻辑检查
执行性能剖测器
测试框架/脚手架
调试器
核对表
要点
第 24 章 重构
24.1 软件演化的类型
软件演化的哲学
24.2 重构简介
重构的理由
代码重复
冗长的子程序
循环过长或嵌套过深
内聚性太差的类
类的接口未能提供层次一致的抽象
拥有太多参数的参数列表
类的内部修改往往被局限于某个部分
变化导致对多个类的相同修改
对继承体系的同样修改
case 语句需要做相同的修改
同时使用的相关数据并未以类的方式进行组织
成员函数使用其他类的特征比使用自身类的特征还要多
过多使用基本数据类型
某个类无所事事
一系列传递流浪数据的子程序
中间人对象无事可做
某个类同其他类关系过于亲密
子程序命名不恰当
数据成员被设置为公用
某个派生类仅使用了基类的很少一部成员函数
注释被用于解释难懂的代码
使用了全局变量
在子程序调用前使用了设置代码
程序员的一些代码似乎是在将来的某个时候才会用到的
核对表
拒绝重构的理由
24.3 特定的重构
数据级的重构
用具名常量代替神秘数值
使变量的名字更为清晰且传递更多信息
将表达式内联化
用函数来代替表达式
引入中间变量
用多个单一用途变量代替某个多用途变量
在局部用途中使用局部变量而不是参数
将基础数据类型转化为类
将一组类型码(type codes)转化为类或枚举类型
将一组类型码转化为一个基类及其相应派生类
将数组转换为对象
把群集(collection)封装起来
用数据类来代替传统记录
语句级的重构
分解布尔表达式
将复杂布尔表达式转换成命名准确的布尔函数
合并条件语句不通部分中的重复代码片段
使用 break 或 return 而不是循环控制变量
子程序级重构
提取子程序或者方法
将子程序的代码内联化
将冗长的子程序转化为类
用简单算法替代复杂算法
增加参数
删除参数
将查询操作从修改操作中独立出来
合并相似的子程序,通过参数区分它们的功能
将行为取决于参数的子程序拆分开来
传递整个对象而非特定成员
传递特定对象而非整个对象
包装向下转型的操作
类实现的重构
将值对象转化为引用对象
将引用对象转化为值对象
用数据初始化替代虚函数
改变成员函数或成员数据的位置
类接口的重构
将成员函数放到另一个类中
将一个类变成两个
删除类
去除委托关系
去掉中间人
用委托代替继承
用继承代替委托
引入外部的成员函数
引入扩展类
对暴露在外的成员变量进行封装
对于不能修改的类成员,删除相关的 set() 成员函数
隐藏那些不会在类之外被用到的成员函数
封装不使用的成员函数
合并那些实现非常类似的基类和派生类
系统级重构
为无法控制的数据创建明确的索引源
将单向的类联系改为双向的类联系
将双向的类联系改为单向的类联系
用 Factory Method 模式而不是简单地构造函数
用异常取代错误处理代码,或者做相反方向的变换
24.4 安全的重构
建议
保存初始代码
重构的步伐请小些
同一时间只做一项重构
把要做的事情一条条列出来
设置一个停车场
多使用检查点
利用编译器警告信息
重新测试
增加测试用例
检查对代码的修改
根据重构风险级别来调整重构方法
不宜重构的情况
不要把重构当做先写后改的代名词
避免用重构代替重写
24.5 重构策略
建议
在增加子程序时进行重构
在添加类的时候进行重构
在修补缺陷的时候进行重构
关注易于出错的模块
关注高度复杂的模块
在维护环境下,改善你手中正在处理的代码
定义清楚干净代码和拙劣代码之间的边界,然后尝试把代码移过这条边界线
核对表
要点
第 25 章 代码调整策略
25.1 性能概述
质量特性和性能
性能和代码调整
程序需求
程序的设计
类和子程序设计
同操作系统的交互
代码编译
硬件
代码调整
25.2 代码调整简介
Pareto 法则
一些无稽之谈
何时调整代码
编译器优化
25.3 蜜糖和哥斯拉
常见的低效率之源
输入/输出操作
系统调用
解释性语言
错误
常见操作的相对效率
25.4 性能测试
性能测量应当精确
25.5 反复调整
25.6 代码调整方法总结
核对表
要点
第 26 章 代码调整技术
26.1 逻辑
在知道答案后停止判断
按照出现频率来调整判断顺序
相似逻辑结构之间的性能比较
用查询表替代复杂表达式
使用惰性求值
26.2 循环
将判断外提
合并
展开
尽可能减少在循环内部做的工作
哨兵值
把最忙的循环放在最内层
削减强度
26.3 数据变换
使用整型数而不是浮点数
数组维度尽可能少
尽可能减少数组引用
使用辅助索引
使用缓存机制
26.4 表达式
利用代数恒等式
削减运算强度
编译期初始化
小心系统函数
使用正确的常量类型
预先算出结果
删除公共子表达式
26.5 子程序
将子程序重写为内联
26.6 用低级语言重写代码
26.7 变得越多,事情反而越没变
核对表
要点
第六部分 系统考虑
第 27 章 程序规模对构建的影响
27.1 交流和规模
27.2 项目规模的范围
27.3 项目规模对错误的影响
27.4 项目规模对生产率的影响
27.5 项目规模对开发活动的影响
活动比例和项目规模
程序、产品、系统和系统产品
方法论和规模
要点
第 28 章 管理构建
28.1 鼓励良好的编码实践
设定标准的考虑事项
鼓励良好的编码实践的技术
给项目的每一部分分派两个人
逐行复查代码
要求代码签名
安排一些好的代码示例供人参考
强调代码是共有财产
奖励好代码
一份简单的标准
28.2 配置管理
什么是配置管理
需求变更和设计变更
遵循某种系统化的变更控制手续
成组地处理变更需求
评估每项变更的成本
提防大量的变更请求
成立变更控制委员会或者类似机构
警惕官僚主义,但也不要因为害怕官僚主义而排斥有效的变更控制
软件代码变更
工具版本
机器配置
备份计划
核对表
28.3 评估构建进度表
评估的方法
建立目标
为评估留出时间,并且做出计划
清楚地说明软件需求
在底层细节层面进行评估
使用若干不同的评估方法,并且比较其结果
定期做重新评估
评估构建的工作量
对进度的影响
评估与控制
如果你落后了该怎么办
28.4 度量
28.5 把程序员当人看
程序员们怎么花费时间
性能差异与质量差异
信仰问题
物理环境
28.6 管理你的管理者
要点
第 29 章 集成
29.1 集成方式的重要性
29.2 集成频率---阶段式集成还是增量集成
阶段式集成
增量集成
增量集成的益处
易于定位错误
及早在项目里取得系统级的成果
改善对进度的监控
改善客户关系
更加充分地测试系统中的各个单元
能在更短的开发进度计划内建造出整个系统
29.3 增量集成的策略
自顶向下集成
自底向上集成
三明治集成
风险导向的集成
功能导向的集成
T-型集成
29.4 Daily Build 与冒烟测试
核对表
要点
第 30 章 编程工具
30.1 设计工具
30.2 源代码工具
集成开发环境(IDE)
针对多个文件的字符串查找和替换
diff 工具
Merge 工具
源代码美化工具
生成接口文档的工具
模版
交叉引用工具
类的继承体系生成器
分析代码质量
吹毛求疵的语法/语义检查器
尺度报告器
重构源代码
重构器
结构改组工具
代码翻译器
Version Control
数据字典
30.3 可执行码工具
产生目标码
编译器和链接器
Build 工具
程序库
代码生成向导
安装
预处理器
调试
测试
代码调整
执行剖测器
汇编代码清单和反汇编
30.4 工具导向的环境
30.5 打造你自己的编程环境
项目特有的工具
脚本
30.6 工具幻境
核对表
要点
第七部分 软件工艺
第 31 章 布局与风格
31.1 基本原则
布局的极端情况
格式化的基本原理
人和计算机对程序的解读
好布局有什么用
把布局作为一种信仰
布局良好的目标
好的布局方案应当是明确的,应当这么做
准确表现代码的逻辑结构
始终如一地表现代码的逻辑结构
改善可读性
经得起修改
如何将布局目标付诸实施
31.2 布局技术
空白
括号
31.3 布局风格
纯块结构
模仿纯块结构
使用 begin-end 对(花括号)指定块边界
行尾布局
哪种风格最优
31.4 控制结构的布局
格式化控制结构块的要点
其他考虑
段落之间要使用空行
对于负责的表达式,将条件分割放在几行上
不用 goto
case 不要有行尾布局的例外
31.5 单条语句的布局
语句长度
用空格使语句显得清楚
使用空格会让逻辑表达式更易读
使用空格让数组引用更容易读
使用空格让子程序参数更容易读
格式化续行
是续行明显
把紧密关联的元素放在一起
将子程序调用的后续行按标准量缩进
让续行的结尾易于发现
将控制语句的后续行缩进以标准的空格数
不要将赋值语句按等号对齐
对赋值语句后续行按标准量缩进
每行仅写一条语句
C++ 中,不要让一行里有多个操作(副作用)
数据声明的布局
每行只声明一个数据
变量声明应尽量接近其首次使用的位置
合理组织声明顺序
31.6 注释的布局
注释的缩进要与相应代码一致
每行注释用至少一个空行分开
31.7 子程序的布局
用空行分隔子程序的各部分
将子程序参数按标准缩进
31.8 类的布局
类接口的布局
类实现的布局
如果文件包含多个类,要清楚地标出每一个类
不要在类里过度注释
文件和程序布局
一个文件应只有一个类
文件的命名应与类名有关
在文件中清晰地分隔各子程序
按字母顺序排列子程序
对于 c++, 请仔细组织源文件中内容的次序
要点
核对表
第 32 章 自说明代码
32.1 外部文档
单元开发文件夹
详细设计文档
32.2 编程风格作文档
32.3 注释或不注释
32.4 高效注释之关键
注释种类
重复代码
解释代码
代码标记
概述代码
代码意图说明
传递代码无法表述的信息
高效注释
采用不会打断或抑制修改的注释风格
用伪代码编程法减少注释时间
将注释集成到你的开发风格中
性能不是逃避注释的好借口
最佳注释量
32.5 注释技术
注释单行
不要随意添加无关注释
行尾注释及其问题
不要对单行代码做行尾注释
不要对多行代码做行尾注释
子主题
何时该用行尾注释
行尾注释用于数据声明
避免用行尾注释存放维护注记
用行尾注释标记块尾
注释代码段
注释应表达代码的意图
代码本身应尽力做好说明
注释代码段时应注重“为何做(why)”而不是“怎么做(how)”
用注释为后面的内容做铺垫
让每个注释都有用
说明非常规做法
别用缩略语
将主次注释区分开
错误或语言环境独特点都要加注释
给违背良好编程风格的理由
不要注释投机取巧的代码,应重写之
注释数据声明
注释数据
注释数值单位
对数值的允许范围给出注释
注释编码含义
注释变量
注释对输入数据的限制
注释“位标志”
将于变量有关的注释通过变量名关联起来
注释全局变量
注释控制结构
应在每个 if、case、循环或者代码段前面加上注释
应在每个控制结构后加上注释
将循环结束处的注释看成是代码太复杂的征兆
注释子程序
指导原则
注释应靠近其说明的代码
在子程序上部用一两句话说明之
在声明参数处注释这些参数
利用诸如 Javadoc 之类的代码说明工具
分清输入和输出数据
注释接口假设
对子程序的局限性作注释
说明子程序的全局效果
记录所用算法的来源
用注释标记程序的各部分
注释类、文件和程序
注释类的一般原则
说明该类的设计方法
说明局限性、用法假设等
注释类接口
不要在类接口处说明实现细节
注释文件的一般原则
说明个文件的意图和内容
将姓名、电子邮件及电话号码放到注释块中
包含版本控制标记
请在注释块中包含法律通告
将文件命名为与其内容相关的名字
程序注释以书本为范例
32.6 IEEE标准
软件开发标准
软件质量保证标准
管理标准
标准综述
核对表-自说明代码
检查表-好的注释技术
要点
第 33 章 个人性格
33.1 个人性格是否和本书话题无关
33.2 聪明和谦虚
33.3 求知欲
在开发过程中建立自我意识
试验
阅读解决问题的有关方法
在行动之前做分析和计划
学习成功项目的开发经验
阅读文档
阅读其他书本期刊
同专业人士交往
向专业开发看齐
第一级:入门级
第二级:中级
第三级:熟练级
第四级:技术带头人
33.4 诚实
33.5 交流与合作
33.6 创造力和纪律
33.7 懒惰
33.8 不如你想象中那样起作用的性格因素
坚持
经验
编程狂人
33.9 习惯
要点
第 34 章 软件工艺的话题
34.1 征服复杂性
34.2 精选开发过程
34.3 首先为人写程序,其次才是为机器
34.4 深入一门语言去编程,不浮于表面
34.5 借助规范集中注意力
34.6 基于问题域编程
将程序划分为不同层次的抽象
第 0 层:操作系统的操作和机器指令
第 1 层:编程语言结构和工具
第 2 层:低层实现结构
第 3 层:低层问题域
第 4 层: 高层问题域
问题域的低层技术
34.7 当心落石
34.8 迭代,反反复复,一次又一次
34.9 汝当分离软件与信仰
软件先知
折中主义
试验
要点
第 35 章 何处有更多信息
35.1 关于软件构建的信息
35.2 构建之外的话题
35.3 期刊
35.4 软件开发者的阅读计划
35.5 参加专业组织
第一部分 打好基础
第1章 欢迎进入软件构建的世界
1.1 什么是软件构建
软件开发中的不同活动
定义问题
需求分析
规划构建
软件架构
详细设计
编码与调试
单元测试
集成测试
系统测试
保障维护
构建活动
编码与调试
详细设计
规划构建
单元测试
集成
集成测试
1.2 软件构建为何如此重要
构建活动是软件开发的主要组成部分
构建活动是软件开发中的核心活动
把主要精力集中于构建活动,可以大大提高程序员的生产率
构建活动的产物-源代码-往往是对软件的唯一精确描述
构建活动是唯一一项确保会完成的工作
1.3 如何阅读本书
要点
软件构建是软件开发的核心活动:构建活动是每个项目中唯一一项必不可少的工作
软件构建的主要活动包括:详细设计,编码,调试,集成,开发者测试(单元测试和集成测试)
构建活动也常被称作"编程"和"编码"
构建活动的质量对软件的质量有实质性的影响
最后,你对"如何进行构建"的理解程度,决定了你这名程序员的优秀程度-这就是本书其余部分的主题了
第2章 用隐喻来更充分地理解软件开发
2.1 隐喻的重要性
2.2 如何使用软件隐喻
2.3 常见的软件隐喻
软件中的书法:写作代码
软件中的耕作法:培植系统
软件的牡蛎养殖观点:系统生长
软件构建:建造软件
应用软件技术:智慧工具箱
组合各个隐喻
要点
隐喻是启示而不是算法.因此它们往往有一点随意
隐喻把软件开发过程与其他你熟悉的活动联系在一起,帮助你更好的理解
有些隐喻比其他一起隐喻更贴切
通过把软件的构建活动比作是房屋的建设过程,我们可以发现,仔细的准备是必要的,而大型项目和小型项目之间也是有差异的
通过把软件开发中的实践比作是智慧工具箱中的工具,我们又发现,每位程序员都有许多工具,但并不存在任何一个能适用于所有工作的工具,因地制宜地选择正确工具是成为能有效编程的程序员的关键
不同的隐喻彼此并不排斥,应当使用对你最有益处的某种隐喻组合.
第3章 三思而后行:前期准备
3.1 前期准备的重要性
前期准备适用于现代软件项目吗
准备工作的中心目标就是降低风险
准备不周全的诱因
开发人员不知道如何进行前期工作
管理者对那些"花时间进行构建活动的前期准备的程序员"的冷漠已经达到了人神共愤的程度
关于开始构建之前要做前期准备的绝对有力且简明的论据
诉诸逻辑
诉诸类比
诉诸数据
在构建活动开始之前清除一个错误,返工的成本仅仅是"在开发过程的最后阶段做同样事情"的十分之一到百分之一
"老板就绪"测试
3.2 辨明你所从事的软件的类型
三种常见的软件项目种类
迭代开发对前期准备的影响
在序列化开发法和迭代式开发法之间做出选择
序列化
需求相当稳定
设计直截了当,而且理解透彻
开发团队对于这一应用领域非常熟悉
项目的风险很小
"长期可预测性"和重要
后期改变需求,设计和编码的代价很可能较昂贵
迭代
需求没有被理解透彻,或者出去其他理由你认为它是不稳定的
设计很复杂,或者有挑战性,或者两者兼具
开发团队对于这一应用领域不熟悉
项目包含很对风险
"长期可预测性"不重要
后期改变需求,设计和编码的代价很可能较低
3.3 问题定义的先决条件
应该用客户的语言来书写,而且应该从客户的角度来描述问题
"未能定义问题"的处罚是,你浪费了大量时间去解决错误的问题
3.4 需求的先决条件
为什么要有正式的需求
有助于确保用户(而不是程序员)驾驭系统的功能
有助于避免争论
重视需求有助于减少开始编程开发之后的系统变更情况
充分详尽地描述需求,是项目成功的关键,它甚至有可能比有效的构建技术而重要.
稳定需求的神话
在构建期间处理需求变更
使用本节末尾的需求核对表来评估你的需求的质量
确保每一个人都知道需求变更的代价
建立一套变更控制程序
使用能适应变更的开发方法
放弃这个项目
注意项目的商业案例
核对表
3.5 架构的先决条件
架构的典型组成部分
程序组织
主要的类
数据设计
业务规则
用户界面设计
资源管理
安全性
性能
可伸缩性
互用性
国际化/本地化
输入输出
错误处理
容错性
架构的可行性
过度工程
关于"买"还是"造"的决策
关于复用的决策
变更策略
架构的总体质量
核对表
3.6 花费在前期准备上的时间长度
更多资源
核对表
要点
构建活动的准备工作的根本目标在于降低风险.要确认你的准备活动是在降低风险,而非增加风险
如何你想开发高质量的软件,软件开发过程必须由始至终关注质量.在项目初期关注质量,对产品质量的正面影响要比在项目末期关注质量的影响要大.
程序员的一部分工作是教育老板和合作者,告诉他们软件开发过程,包括在开始编程之前进行充分准备的重要性
你所从事的软件项目的类型对构建活动的前期准备有重大影响--许多项目应该是高度迭代式的,某些应该是序列式的.
如果没有明确的问题定义,那么你可能在构建期间解决错误的问题
如果没有做完良好的需求分析工作,你可能没能察觉待解决问题的重要细节.如何需求变更发生在构建之后的阶段,其代价是"在项目早期更改需求"的20至100倍.因此在开始编程之前,你要确认"需求"已经到位了.
理解项目的前期准备所采用的方法,并相应地选择构建方法.
第4章 关键的"构建"决策
4.1 选择编程语言
语言描述
4.2 编程约定
4.3 你在技术浪潮中的位置
"深入一种语言去编程"的例子
4.4 选择主要的构建实践方法
核对表
要点
每种编程语言都有其优点和弱点.要知道你使用的语言的明确优点和弱点.
在开始编程之前,做好一些约定(convention)。“改变代码使之符合这些约定”是近乎不可能的。
“构建的实践方法”的种类比任何单个项目能用到的要多。有意识地选择最适合你的项目的实践方法。
问问你自己,你采用的编程实践是对你所用的编程语言的正确响应,还是受它的控制?请记得“深入一种语言去编程”,不要仅“在一种语言上编程”。
你在技术浪潮中的位置决定了哪种方法是有效的-甚至是可能用到的。确定你在技术浪潮中的位置,并相应调整计划和预期目标。
第二部分 创建高质量的代码
第5章 软件构建中的设计
5.1 设计中的挑战
设计是一个险恶的问题
设计是个了无章法的过程(即使它能得出清爽的成果)
设计就是确定取舍和调整顺序的过程
设计受到诸多限制
一部分是创造可能发生的事情
另一部分是在限制可能发生的事情
设计是不确定的
设计是一个启发式过程
设计是自然而然形成的
5.2 关键的设计概念
软件的首要技术使命:管理复杂度
偶然的难题和本质的难题
本质:一件事物必须具备的属性
偶然:一件事物碰巧具备的属性
管理复杂度的重要性
如何应对复杂度
高代价,低效率
用复杂的方法解决简单的问题
用简单但错误的方法解决复杂的问题
用不恰当的复杂方法解决复杂的问题
管理复杂度
把任何人在同一时间需要处理的本质(essential)复杂度的量减到最小
不要让偶然性(accidental)的复杂度无谓地快速增长
理想的设计特征
最小的复杂度
易于维护
松散耦合
可扩展性
可重用性
高扇入
让大量的类使用某个给定的类
低扇出
让一个类里少量或适中地使用其他的类
可移植性
精简性
设计的系统没有多余的部分
层次性
尽量保持系统各个分解层的层次性
标准技术
尽量用标准化的,常用的方法,让整个系统给人一种熟悉的感觉
设计的层次
第1层 软件系统
第2层 分解为子系统或包
常用的子系统
业务规则
用户界面
数据库访问
对系统的依赖性
第3层 分解为类
类与对象的比较
第4层 分解成子程序
第5层 子程序内部的设计
5.3 设计构造块:启发式方法
找出现实世界中的对象
辨识对象及其属性
确定可以对各个对象进行的操作
确定各个对象能对其他对象进行的操作
确定对象的哪些部分对其他对象可见--哪些部分可以是公用的,哪些部分应该是私用的
定义每个对象的公开接口
形成一致的抽象
封装实现细节
当继承能简化设计时就继承
隐藏秘密(信息隐藏)
秘密和隐私权
信息隐藏的一个例子
两种秘密
隐藏复杂度
隐藏变化源
信息隐藏的障碍
信息过度分散
循环依赖
把类内数据误认为全局数据
可以察觉的性能损耗
信息隐藏的价值
是结构化程序设计和面向对象设计的根基之一
能够激发出有效的设计方案
有助于设计类的公开接口
找出容易改变的区域
应对措施
1. 找出看起来容易变化的项目
2. 把容易变化的项目分离出来
3. 把看起来容易变化的项目隔离开来
业务规则
对硬件的依赖性
输入和输出
非标准的语言特性
困难的设计区域和构建区域
状态变量
不要使用布尔变量作为状态变量,请换用枚举类型
使用访问器子程序取代对状态变量的直接检查
数据量的限制
预料不同程度的变化
保持松散耦合
耦合标准
规模
模块之间的连接数
可见性
两个模块之间的连接的显著程度
灵活性
模块之间的连接是否容易改动
耦合的种类
简单数据参数耦合
简单对象耦合
对象参数耦合
语义上的耦合
查阅常用的设计模式
设计模式通过提供现成的抽象来减少复杂度
设计模式通过把常见解决方案的细节予以制度化来减少出错
通过提供多种设计方案而带来启发性的价值
通过设计对话提升到一个更高层次上来简化交流
其他的启发式方法
高内聚性
构造分层结构
严格描述类契约
分配职责
为测试而设计
避免失误
有意识地选择绑定时间
创建中央控制点
考虑使用蛮力突破
画一个图
保持设计的模块化
使用启发式方法的原则
5.4 设计实践
迭代
分而治之
自上而下和自下而上的设计方法
自上而下的论据
自下而上的论据
其实并没有争议
建立实验性原型
合作设计
要做多少设计才够
记录你的设计成果
把设计文档插入到代码里
用Wiki来记录设计讨论和决策
写总结邮件
使用数码相机
保留设计挂图
使用CRC(类、职责、合作者)卡片
在适当的细节层创建UML图
5.5 对流行的设计方法的评论
更多资源
软件设计,一般性问题
软件设计理论
设计模式
广义上的设计
标准
核对表
要点
软件的首要技术使命就是管理复杂度。以简单性作为努力目标的设计方案对此最有帮助
简单性可以通过两种方式来获取
减少在同一时间所关注的本质性复杂度的量
避免生成不必要的偶然的复杂度
设计是一种启发式的过程。固执于某一种单一方法会损害创新能力,从而损害你的程序
好的设计都是迭代的。你尝试设计的可能性越多,你的最终设计方案就会变得越好。
信息隐藏是个非常有价值的概念。通过询问“我应该隐藏些什么?”能够解决很多困难的设计问题。
很多有用有趣的、关于设计的信息存在于本书之外。这里所给出的观点知识对这些有价值资源的一点提示而已
第6章 可以工作的类
6.1 类的基础:抽象数据类型(ADTs)
含义
一些数据以及对这些数据所进行的的操作的集合
需要用到ATD的例子
使用ATD的益处
可以隐藏实现细节
改动不会影响到整个程序
让接口能提供更多信息
更容易提高性能
让程序的正确性更显而易见
程序更具自我说明性
无须在程序内到处传递数据
你可以像在现实世界中那样操作实体,而不是在底层实现上操作它
更多的ADT示例
通过研究例子,可以得出一些指导建议
把常见的底层数据类型创建为ADT并使用这些ADT,而不再使用底层数据类型
把像文件这样的常用对象当成ADT
简单的事物也可当做ADT
不要让ADT依赖于其存储介质
在非面向对象的环境中用ADT处理多分数据实例
ADT和类
6.2 良好的类接口
好的抽象
好的抽象
类的接口应该展现一致的抽象层次
一定要理解类所实现的抽象是什么
换成成对的服务
把不相关的信息转移到其他类中
尽可能让接口可编程,而不是表达语义
谨防在修改时破坏接口的抽象
不要添加与接口抽象不一致的公用成员
同时考虑抽象性和内聚性
良好的封装
尽可能地限制类和成员的可访问性
不要公开暴露成员数据
避免把私用的实现细节放入到类的接口中
不要对类的使用者做出任何假设
避免使用友元类
不要因为一个子程序里仅使用公用子程序,就把它归入公开接口
让阅读代码比编写代码更方便
要格外警惕从语义上破坏封装性
留意过于紧密的耦合关系
6.3 有关设计和实现的问题
包含(“有一个·····”的关系)
通过包含来实现“有一个/has a”的关系
在万不得已时通过private继承来实现“有一个”的关系
警惕有超过约7个数据成员的类
继承(“是一个······”关系)
使用继承注意事项
用public 继承来实现“是一个”的关系
要么使用继承并进行详细说明,要么就不要用它
遵循Liskov替换原则
确认只继承需要继承的部分
不要“覆盖”一个不可覆盖的成员函数
把共用的接口、数据及操作放到继承树中尽可能高的位置
只有一个实例的类是值得怀疑的
只有一个派生类的基类也值得怀疑
派生后覆盖了某个子程序,但在其中没做任何操作,这种情况也值得怀疑
避免让继承体系过深
尽量使用多态,避免大量的类型检查
让所有数据都是private(而非protected)
多重继承
为什么有这么多关于继承的规则
成员函数和数据成员
让类中子程序的数量尽可能少
禁止隐式地产生你不需要的成员函数和运算符
减少类所调用的不同子程序的数量
对其他类的子程序的间接调用要尽可能少
一般来说,应尽可能减少类和类之间相互合作的范围
构造函数
如果可能,应该在所有的构造函数中初始化所有的数据成员
用私用(private)构造函数来强制实现单件属性(singleton property)
优先采用深层复本(deep copies),除非论证可行,才采用浅层复本(shallow copies)
6.4 创建类的原因
创建类的合理原因
为现实世界中的对象建模
为抽象的对象建模
降低复杂度
隔离复杂度
隐藏实现细节
限制变动的影响范围
隐藏全局数据
让参数传递更顺畅
建立中心控制点
让代码更易于重用
为程序族做计划
把相关操作包装到一起
实现某种特定的重构
应该避免的类
避免创建万能类
消除无关紧要的类
避免用动词命名的类
6.5 与具体编程语言相关的问题
6.6 超越类:包
核对表
要点
第7章 高质量的子程序
7.1 创建子程序的正当理由
理由
降低复杂度
引入中间、易懂的抽象
避免代码重复
支持子类化(subclassing)
隐藏顺序
隐藏指针操作
提交可移植性
简化复杂的布尔判断
改善性能
确保所有的子程序都很小
似乎过于简单而没必要写成子程序的操作
7.2 在子程序层上设计
内聚
功能上的内聚性
顺序上的内聚性
通信上的内聚性
临时的内聚性
过程上的内聚性
逻辑上的内聚性
巧合的内聚性
7.3 好的子程序名字
描述子程序所做的所有事情
避免使用无意义的、模糊或表述不清的动词
不要仅通过数字来形成不同的子程序名字
根据需要确定子程序名字的长度
给函数命名是要对返回值有所描述
给过程起名时使用语气强烈的动词加宾语的形式
准确使用对仗词
为常用操作确立命名规则
7.4 子程序可以写多长
7.5 如何使用子程序参数
一些可以减少接口错误的指导原则
安装输入-修改-输出的顺序排列参数
考虑自己创建 in 和 out 关键字
如果几个子程序都用了类似的一些参数,应该让这些参数的排列顺序保持一致
使用所有的参数
把状态或出错变量放到最后
不要把子程序的参数用作工作变量
在接口中对参数的假定加以说明
把子程序的参数个数限制在大约7个以内
考虑对参数采用某种表示输入、修改、输出的命名规则
为子程序传递用以维持其接口抽象的变量或对象
使用具名参数
确保实际参数与形式参数相匹配
7.6 使用函数时要特别考虑的问题
什么时候使用函数,什么时候使用过程
设置函数的返回值
检查所有可能的返回路径
不要返回指向局部数据的引用或指针
7.7 宏子程序和内联子程序
预处理器
把宏表达式整个包含在括号内
把含有多条语句的宏用大括号括起来
用给子程序命名的方法来给展开后代码形同子程序的宏命名,以便在需要时可以用子程序来替换宏
宏子程序在使用上的限制
内联子程序
节制使用 inline 子程序
核对表
要点
第8章 防御式编程
8.1 保护程序免遭非法输入数据的破坏
通常有三种方法来处理进来垃圾的情况
检查所有来源于外部的数据的值
检查子程序所有输入参数的值
决定如何处理错误的输入数据
8.2 断言
建立自己的断言机制
使用断言的指导建议
用错误处理代码来处理预期会发生的状况,用断言来处理绝不应该发生的状况
避免把需要执行的代码放到断言中
用断言来注解并验证前条件和后条件
对于高健壮性的代码,应该先使用断言在处理错误
8.3 错误处理技术
可用的技术
返回中立值
换用下一个正确的数据
返回与前次相同的数据
换用最接近的合法值
把警告信息记录到日志文件中
返回一个错误码
调用错误处理子程序或对象
当错误发生时显示出错信息
用最妥当的方式在局部处理错误
关闭程序
健壮性与正确性
正确性:永不返回不准确的结果
健壮性:要不断尝试采取某些措施,以保证软件可以持续地运转下去,哪怕有时做出一些不够准确的结果
高层次设计对错误处理方式的影响
8.4 异常
用异常通知程序的其他部分,发生了不可忽略的错误
只在真正例外的情况下才抛出异常
不能用异常来推卸责任
避免在构造函数和析构函数中抛出异常
在恰当的抽象层次抛出异常
在异常消息中加入关于导致异常发生的全部信息
避免使用空的catch语句
了解所用函数库可能抛出的异常
考虑创建一个集中的异常报告机制
把项目中对异常的使用标准化
考虑异常的替换方案
8.5 隔离程序,使之包容有错误造成的损害
隔离
在输入数据时将其转换为恰当的类型
隔栏与断言的关系
8.6 辅助调试的代码
不要自动地把产品版的限制强加于开发版之上
尽早引入辅助调试的代码
采用进攻时编程
计划移除调试辅助的代码
使用类似 ant 和 make 这样的版本控制工具和 mark 工具
使用内置的预处理器
编写你自己的预处理器
使用调试存根(debuging stubs)
8.7 确定在产品代码中该保留多少防御式代码
建议
保留那些检查重要错误的代码
去掉检查细微错误的代码
去掉可以导致程序硬性崩溃的代码
保留可以让程序稳妥地崩溃的代码
为你的技术支持人员记录错误信息
确认留着代码中的错误消息是友好的
8.8 对防御式编程采用防御的姿态
核对表
要点
第9章 伪代码编程过程
9.1 创建高质量的类
创建一个类的步骤
创建类的总体设计
创建类中的子程序
复审并测试整个类
创建子程序的步骤
9.2 伪代码
9.3 通过伪代码编程过程创建子程序
设计子程序
检查先决条件
定义子程序要解决的问题
为子程序命名
决定如何测试子程序
在标准库中搜寻可用的功能
考虑错误处理
考虑效率问题
研究算法和数据类型
编写伪代码
考虑数据
检查伪代码
在伪代码中试验一些想法,留下最好的想法(迭代)
编写子程序的代码
写出子程序的声明
把伪代码转变为高层次的注释
在每条注释下面填充代码
检查代码是否需要进一步分解
检查代码
在脑海里检查程序中的错误
编译子程序
在调试器中逐行执行代码
测试代码
消除程序中的错误
收尾工作
根据需要重复上述步骤
9.4 伪代码编程过程的替代方案
测试先行开发
重构
契约式设计
东拼西凑
核对表
要点
第三部分 变量
第10章 使用变量的一般事项
10.1 数据认知
10.2 轻松掌握变量定义
隐式声明
关闭隐式声明
声明全部的变量
遵循某种命名规则
检查变量名
10.3 变量初始化原则
避免产生初始化错误的建议
在声明变量的时候初始化
在靠近变量的第一次使用的位置初始化它
理想情况下,在靠近第一次使用变量的位置声明和定义该变量
在可能的情况下使用 final 或者 const
特别注意计数器和累加器
在类的构造函数里初始化该类的数据成员
检查是否需要重新初始化
一次性初始化具名常量:用可执行代码来初始化变量
使用编译器设置来自动初始化所有变量
利用编译器的警告信息
检查输入参数的合法性
使用内存访问检查工具来检查错误的指针
在程序开始时初始化工作内存
10.4 作用域
使变量引用局部化
尽可能缩短变量的“存活”时间
测量变量的生存时间
减少作用域的一般原则
在循环开始之前再去初始化该循环里使用的变量,而不是在该循环所属的子程序的开始处初始化这些变量
直到变量即将被使用时再为其赋值
把相关语句放到一起
把相关语句组提取成单独的子程序
开始时采用最严格的可见性,然后根据需要扩展变量的作用域
有关缩小变量作用域的说明
10.5 持续性
10.6 绑定时间
10.7 数据类型和控制结构之间的关系
三种类型的数据和相应控制结构之间的关系
序列型数据翻译为程序中的顺序语句
选择型数据翻译为程序中的 if 和 case 语句
迭代型数据翻译成程序中的 for 、repeat、while 等循环结构
10.8 为变量指定单一用途
每个变量只用于单一用途
避免让代码具有隐含含义
确保使用了所有已声明的变量
核对表
要点
第11章 变量名的力量
11.1 选择好变量名的注意事项
最重要的命名注意事项
以问题为导向
最适当的名字长度
变量名对作用域的影响
变量命中的计算值限定词
变量名中的常用对仗词
11.2 为特定类型的数据命名
为循环下标命名
为状态变量命名
为临时变量命名
为布尔变量命名
谨记典型的布尔变量名
done
error
found
success
给布尔变量赋予隐含“真/假”含义的名字
使用肯定的布尔变量名
为枚举类型命名
为常量命名
11.3 命名规则的力量
为什么要有规则
何时采用命名规则
正式程度
11.4 非正式命名规则
与语言无关的命名规则的指导原则
区分变量名和子程序名称
区分类和对象
标识全局变量
标识成员变量
标识类型声明
标识具名常量
标识枚举类型的元素
在不能保证输入参数只读的语言里标识只读参数
格式化命名以提高可读性
与语言相关的命名规则的指导原则
c 的命名规则
c++ 的命名规则
Java 的规则
Visual Basic 的命名规则
混合语言编程的注意事项
命名规则示例
11.5 标准前缀
用户自定义类型缩写
语义前缀
标准前缀的优点
11.6 创建具备可读性的短名字
缩写的一般指导原则
语音缩写
有关缩写的评论
不要用从每个单词中删除一个字符的方式来缩写
缩写要一致
创建你能读出来的名字
避免使用容易看错或者读错的字符组合
使用辞典来解决命名冲突
在代码里用缩写对照表解释极短的名字的含义
在一份项目级“标准缩写”文档中说明所有的缩写
记住,名字对代码读者的意义要比对作者更重要
11.7 应该避免的名字
避免使用令人误解的名字或缩写
避免使用具有相似含义的名字
避免使用具有不同含义但却有相似名字的变量
避免使用发音近似的名字
避免在名字中使用数字
避免在名字中拼错单词
避免使用英语中常常拼错的单词
不要仅靠大小写来区分变量名
避免使用多种自然语言
避免使用标准类型、变量和子程序的名字
不要使用与变量含义完全无关的名字
避免在名字中包含易混淆的字符
核对表
要点
第12章 基本数据类型
12.1 数值概论
建议
避免使用“神秘数值(magic number)”
如果需要,可以使用硬编码的 0 和 1
预防除零(devide-by-zero)错误
使类型转换变得明显
避免混合类型的比较
注意编译器的警告
12.2 整数
注意事项
检查整数除法
检查整数溢出
检查中间结果溢出
12.3 浮点数
指导原则
避免数量级相差巨大的数之间的加减运算
避免等量判断
处理舍入误差问题
检查语言和函数库对特定数据类型的支持
12.4 字符和字符串
技巧
避免使用神秘字符和神秘字符串
避免 off-by-one 错误
了解你的语言和开发环境是如何支持 Unicode 的
在程序生命期中尽早决定国际化/本地化策略
如何你知道只需要支持一种文字的语言,请考虑使用 ISO 8859 字符集
如何你需要支持多种语言,请使用 Unicode
采用某种一致的字符串类型转换策略
C 语言中的字符串
注意字符串指针和字符数组之间的差异
把 C-style 字符串的长度声明为 CONSTANT + 1
用 null 初始化字符串以避免没有终端的字符串
用字符数组取代 C 中的指针
用 strncpy() 取代 strcpy() 以避免无终端的字符串
12.5 布尔变量
仔细运用
用布尔变量对程序加以文档说明
用布尔变量是简化复杂的判断
如果需要的话,创建你自己的布尔类型
12.6 枚举类型
指导原则
用枚举类型来提高可读性
用枚举类型来提高可靠性
用枚举类型来简化修改
将枚举类型作为布尔变量的替换方案
检查非法数值
定义出枚举的第一项和最后一项,以便用于循环边界
把枚举类型的第一个元素留作非法值
明确定义项目代码编写标准中第一个和最后一个元素的使用规则,并且在使用时保持一致
警惕给枚举元素明确赋值而带来的失误
如果你的语言里没有枚举类型
12.7 具名常量
12.8 数组
使用数组的一些建议
确认所有的数组下标都没有超出数组的边界
考虑用容器来取代数组,或者将数组作为顺序化结构来处理
检查数组的边界点
如果数组是多维的,确认下标的使用顺序是正确的
提防下标串话
12.9 创建你自己的类型(类型别名)
创建自己类型的原因
易于修改
避免过多的信息分发
增加可靠性
弥补语言的不足
为什么创建自己的类型的示例使用 Pascal 和 Ada 写的
创建自定义数据类型的指导原则
给所创建的类型取功能导向的名字
避免使用预定义类型
不要重定义一个预定义的类型
定义替代类型以便于移植
考虑创建一个类而不是使用 typedef
核对表
要点
第13章 不常见的数据类型
13.1 结构体
一些使用结构体的理由
用结构体来明确数据关系
用结构体简化对数据块的操作
用结构体来简化参数列表
用结构体来减少维护
13.2 指针
用来理解指针的范例
内存中的位置
如何解释指针所指的内容
使用指针的一般技巧
把指针操作限制在子程序或者类里面
同时声明和定义指针
在与指针分配相同的作用域中删除指针
在使用指针之前检查指针
先检查指针所引用的变量在使用它
用狗牌字段来检测损毁的内存
增加明显的冗余
用额外的指针变量来提高代码清晰度
简化复杂的指针表达式
画一个图
按照正确的顺序删除链表中的指针
分配一片保留的内存后备区域
粉碎垃圾数据
再删除或释放指针之后把它们设为空值
在删除变量之前检查非法指针
跟踪指针分配情况
编写覆盖子程序,集中实现避免指针问题的策略
采用非指针的技术
c++ 指针
c 指针
13.3 全局数据
与全局数据有关的常见问题
无意间修改了全局数据
与全局数据有关的奇异的和令人激动的别名问题
与全局数据有关的代码重入( re-entrant )问题
全局数据阻碍代码重用
与全局数据有关的非确定的初始化顺序事宜
全局数据破坏了模块化和智力上的可管理性
使用全局数据的理由
保存全局数据
模拟具名常量
模拟枚举类型
简化对极其常用的数据的使用
消除流浪数据
只有万不得已时才使用全局变量
首先把每一个变量设置为局部的,仅当需要时才把变量设置为全局的
区分全局变量和类变量
用访问器子程序来取代全局数据
访问器子程序的优势
如何使用访问器子程序
要求所有的代码通过访问器子程序来存取数据
不要把你所有的全局数据都扔在一处
用锁定来控制对全局变量的访问
在你的访问器子程序里构建一个抽象层
使得对一项数据的所有访问都发生在同一个抽象层上
如何降低使用全局数据的风险
创建一种命名规则来突出全局变量
为全部的全局变量创建一份注释良好的清单
不要用全局变量来存放中间结果
不要把所有的数据都放在一个大对象中并到处传递,以说明你没有使用全局变量
核对表
要点
第四部分 语句
第 14 章 组织直线型代码
14.1 必须有明确顺序的语句
组织语句的简单原则
设法组织代码,使依赖关系变得非常明显
使子程序名能突显依赖关系
使用子程序参数明确显示依赖关系
用注释对不清晰的依赖关系进行说明
用断言或者错误处理代码来检查依赖关系
14.2 顺序无关的语句
使代码易于自上而下地阅读
把相关的语句组织在一起
核对表
要点
第 15 章 使用条件语句
15.1 if 语句
简单 if-then 语句
把正常情况的处理放在 if 后面而不要放在 else 后面
让 if 子句后面跟随一个有意义的语句
考虑 else 子句
测试 else 子句的正确性
检查 if 和 else 子句而不是弄反了
if-then-else 语句串
利用布尔函数调用简化复杂的检测
把最常见的情况放在最前面
确保所有的情况都考虑到了
如果你的语言支持,请把 if-then-else 语句串替换成其他结构
15.2 case 语句
为 case 选择最有效的排列顺序
按字母顺序或数字顺序排列各种情况
把正常的情况放在前面
按执行频率排列 case 子句
使用 case 语句的诀窍
简化每种情况对应的操作
不要为了使用 case 语句而刻意制造一个变量
用 default 子句只用于检查真正的默认情况
利用 default 子句来检测错误
在 c++ 和 java 里,避免代码执行越过一条 case 子句的末尾
在 c++ 里,在 case 末尾明确无误地标明需要穿越执行的程序流程
核对表
要点
第 16 章 控制循环
16.1 选择循环的种类
什么时候使用 while 循环
检测位于循环的开始
检测位于循环的结尾
什么时候用带退出的循环
正常的带退出循环
非正常的带退出循环
何时使用 for 循环
何时使用 foreach 循环
16.2 循环控制
进入循环
只从一个位置进入循环
把初始化代码仅放在循环前面
用 while(true) 表示无限循环
在适当的情况下多使用 for 循环
在 while 循环更适用的时候,不要使用 for 循环
处理好循环体
用 “{”和 “}”把循环中的语句括起来
避免空循环
把循环内务操作要么放在循环的开始,要么放在循环的末尾
一个循环只做一件事
退出循环
如何处理循环尾
设法确认循环能够终止
使循环终止条件看起来很明显
不要为了终止循环而胡乱改动 for 循环的下标
避免出现依赖于循环下标最终取值的代码
考虑使用安全计数器
提前退出循环
考虑在 while 循环中使用 break 语句而不用布尔标记
小心那些有很多 break 散布其中的循环
在循环开始处用 continue 进行判断
如果语言支持,请使用带标号 break 结构
使用 break 和 continue 时要小心谨慎
检查端点
使用循环变量
用整数或者枚举类型表示数组和循环的边界
在嵌套循环中使用有意义的变量名来提高可读性
用有意义的名字来避免循环下标串话
把循环下标变量的作用域限制在本循环内
循环应该有多长
循环要尽可能地短,以便能够一目了然
把嵌套限制在 3 层以内
把长循环的内容移到子程序里
要让长循环格外清晰
16.3 轻松创建循环---由内而外
16.4 循环和数组的关系
核对表
要点
第 17 章 不常见的控制结构
17.1 子程序的多处返回
一些 return 语句的指导原则
如果能增强可读性,那么就使用 return
用防卫子句(guard clause)(早返回或退出) 来简化复杂的错误处理
减少每个子程序中 return 的数量
17.2 递归
递归的例子
使用递归的技巧
确认递归能够停止
使用安全计数器防止出现无穷递归
把递归限制在一个子程序内
留心栈空间
不要用递归去计算阶乘或者斐波那契数列
17.3 goto
反对 goto 的论点
支持 goto 的观点
关于 goto 的虚假辩论
错误处理和 goto
重写策略
用嵌套的 if 语句重写
用一个状态变量重写代码
用 try-finally 重写
各方法之间的比较
goto 和在 else 子句中的共享代码
goto 和在 else 子句中的共享代码
goto 使用原则总结
17.4 针对不常见控制结构的观点
核对表
要点
第 18 章 表驱动法
18.1 表驱动法使用总则
使用表驱动法的两个问题
你必须要回答怎样从表中查询条目的问题
你应该在表里面存些什么
18.2 直接访问表
示例:一个月中的天数
示例:保险费率
例子:灵活的消息格式
基于逻辑的方法
面向对象的方法
表驱动法
构造查询键值
几种方法
复制信息从而能够直接使用键值
转换键值以使其能够直接使用
把键值转换提取成独立的子程序
18.3 索引访问表
需要注意的一些细节
留心端点
考虑使用二分查找取代顺序查找
考虑用索引访问来取代阶梯技术
把阶梯表查询操作提取成单独的子程序
18.4 阶梯访问表
18.5 表查询的其他示例
核对表
要点
第 19 章 一般控制问题
19.1 布尔表达式
用 true 和 false 做布尔判断
隐式地比较布尔值与 true 和 false
简化复杂的表达式
拆分复杂的判断并引入新的布尔变量
把复杂的表达式做成布尔函数
用决策表代替复杂的条件
编写肯定形式的布尔表达式
在 if 语句中,把判断条件从否定形式转换为肯定形式,并且互换 if 和 else 子句中的代码
用狄摩根定理简化否定的布尔判断
用括号使布尔表达式更清晰
用一种简单的计数技巧来使括号对称
把布尔表达式全括在括号里面
理解布尔表达式是如何求值得
按照数轴的顺序编写数值表达式
与 0 比较的指导原则
隐式地比较逻辑变量
把数和 0 相比较
在 C 中显示地比较字符和零终止符 ('\0')
把指针和 NULL 相比较
布尔表达式的常见问题
在 C 家族语言中,应该吧常量放在比较的左端
在 c++中,可以考虑创建预处理宏来替换&&,||和==(不得已才这么做)
在 Java 中,应理解 a==b 和 a.equals(b) 之间的差异
19.2 复合语句(语句块)
把括号对一起写出
用括号来把条件表达清楚
19.3 空语句
小心使用空语句
为空语句创建一个 DoNothing() 预处理宏或者内联函数
考虑如果换一个非空的循环体,是否会让代码更清晰
19.4 驯服危险的深层嵌套
一些避免深层嵌套的方法
通过重复检测条件中的某一部分来简化嵌套的 if 语句
用 break 块来简化嵌套 if
把嵌套 if 转换成一组 if-then-else 语句
把嵌套 if 转换成 case 语句
把深层嵌套的代码抽取出来放进单独的子程序
使用一种更面向对象的方法
重新设计深层嵌套的代码
对减少嵌套层次的技术的总结
19.5 编程基础:结构化编程
结构化编程的三个组成部分
顺序
选择
迭代
19.6 控制结构与复杂度
复杂度的重要性
降低复杂度的一般原则
如何度量复杂度
如果处理复杂度的度量结果
其他类型的复杂度
核对表
要点
收藏
收藏
0 条评论
下一页