第二十章: 软件质量概述
20.1 软件质量的特性
外在特性:
- 正确性
- 可用性
- 效率
- 可靠性
- 完整性:包括输入数据验证
- 适应性
- 精确
- 健壮
内在特性:
- 可维护性
- 灵活性
- 可移植性
- 可重用
- 可读性
- 可测试性
- 可理解性
20.2 改善软件质量的技术
- 明确软件质量的目标
- 明确定义质量保证工作,把保证程序的质量放到第一位
- 测试策略?
- 非正式的技术复查:代码都过一遍
- 正式的技术复查
- 对变更进行控制
- 结果量化
- 制作原型(demo)
第二十一章 : 协同构建
21.1 协同开发实践概要
第二十二章: 测试
第二十三章: 调试
23.1效率低下的调试方式
-
凭猜测 -
不理解问题就去找问题,去解决 用特殊情况去解决问题,治标不治本迷信式编程
23.2 寻找问题所在
- 找错误
- 把错误的发生稳定下来(让BUG必现)
- 确定原因
23.3 修正缺陷
- 解决前理解了问题出现的原因
- 理解程序本身
- 修改的部分要有充分的理由
- 一次只做一个动作
- 在工程中寻找还有没有类似的错误
23.4 调试中的心理因素
23.5 调试工具
- 把编译器的警告级别设置为最高
- 用对待错误的态度来处理警告
第二十四章:重构
24.1 软件演化类型
- 程序的质量在演化过程中是降低了还是提高了
- 演化是源于程序构建过程中的修改,还是维护过程中的修改
24.2 重构简介
在不改变软件外部行为的前提下,对其内部结构进行改变,使其共容易理解并便于修改
重构的理由
- 代码重复
- 冗长的子程序
- 循环过长或者嵌套过深
- 内聚性太差的类
- 类结构抽象层次不统一
- 函数参数列表过长
- 类的内部修改被局限于一部分,这表明一个类里有俩个相互独立的概念再工作
- 变化导致多个类需要修改
- 添加子类的时候需要对另一个子类去修改
- case语句要做相同的修改
- 对经常操作的一组数据进行类划
- 类的成员函数功能更接近于其他类
- 某些类看起来无所事事
- 当一个数据通过多个子程序进行转传时,考虑这个数据是否符合这些子程序的抽象概念
- 中间人对象无事可做:代码经常调用成员函数,但是成员没什么用
- 俩个类的联系过与亲密,破坏了封装性
- 子程序命名不当
- 数据成员被设置为公用:要考虑用访问器子程序去访问private数据
- 某个派生类只用了基类的很少一部分成员函数:这表明这个派生类可能是基类的一部分数据成员,而不是is-a的关系
- 用注释去解释难懂的代码
- 使用了全局变量
- 超前设计,给程序中添加未来可能需要的功能
24.3 特定的重构
数据级别的重构
- 用具名常量代替神秘数值
- 变量名合适
- 把一个中间变量换成给他赋值的那个表达式本身
- 用函数来代表表达式
- 合适的时候引人中间变量
- 分解多用途变量
- 子程序中用局部变量而不是把参数当作局部变量
-
把基础数据类型转化为类
语句级的重构
- 分解布尔表达式
- 将复杂布尔表达式转换为命名准确的布尔函数
- 合并重复代码
- 用多态来替代条件语句
子程序级重构
- 提炼子程序或者方法
- 子程序的代码内联
- 冗长的子程序转为类
- 用更简单的算法来代替复杂算法
- 通过添加参数的形式去获取更多信息
- 不需要的参数及时删除
- 合并相似子程序,通过参数区分功能
- 函数行为取决于参数的程序拆分成不同的子行为
- 需要的话 传递整个对象而不是对象的多个特定数据
类级别的重构
-
将值对象转化为引用对象:多个一模一样的大型复杂对象,只保存一份主拷贝,然后其他地方使用该对象的引用
-
将引用对象转化为值对象
-
通过分析设计,调整成员函数和成员数据的位置,例如把子程序,成员上移到基类中
-
基类中的一部分代码只有部分实例用到,则提取到派生类中
-
子类的相似功能代码放到基类
类接口的重构
- 去除类之间的委托关系,可以考虑让原基类成为类的成员函数,并开放一些子函数,实现内聚的抽象
- 去掉中间人 比如A.B.C 如果调用B只是为了调用C 考虑要不要去掉B
- 对暴露在外的成员变量进行封装
- 封装不使用的成员函数
系统级的重构
- 为无法控制的数据创建明确的索引值,类似提供一个统一的数据入口
- 用工厂方法模式,而不是简单的使用构造函数
- 用异常取代错误处理代码
24.4 安全的重构
- 保存原始代码
- 保持小的重构步伐
- 同一时间只做一项重构
- 把要做的事情一件件列出来
- 调整重构优先级
- 检查对代码的修改
24.5 重构策略——满足28法则
- 在增加子程序时候进行重构
- 在添加类的时候进行重构
- 在修改缺陷的时候进行重构
- 关注那些易于出错的模块
- 关注高复杂度的模块
第二十五章:代码调整策略
25.1 性能概述
性能和代码调整
优先要考虑的事情:
- 程序需求:明确对性能的优化是基于需求的
- 程序的设计是否合理:
- 类子程序的设计
- 代码编译
- 硬件
- 代码调整
25.2 代码调整简介
程序员们首先应该实现程序应该具备的所有功能,然后再使程序臻于完美,而这时,需要精益求精的部分通常是很少的。
错误的认知:
代码行数越少性能越高特定运算可能比其他的快应当随时随地的进行优化程序运行速度同其正确性同样重要!
25.3 常见低效之源
- 输入输出操作
- 系统调用
- 解释型语言
- 错误
25.4 性能测量
性能优化应该是基于性能测量的结果上的
25.5 反复调整
25.6 方法总结
- 用设计良好的代码来开发软件,使程序易于理解和修改
- 保存代码原版
- 对系统进行分析测量,找出性能瓶颈
- 确认性能问题是否由于设计,数据类型或者算法上的缺陷
- 调整
- 调整后测量
- 无效则回滚
第二十六章:代码调整技术
26.1 逻辑
- 按照出现频率来调整判断顺序
- 用查询表代替复杂表达式
- 惰性求值:有些数据可以考虑在用到的时候计算,并进行缓存
26.2 循环
- 将判断外提
- 合并
- 展开
- 尽可能减少在循环内部做的工作
- 嵌套循环中把最忙的循环放在最内层,可以减少循环次数
26.3 数据变换
- 使用整数而不是浮点数
- 数组维度尽可能少
- 使用辅助索引
- 使用缓存机制
26.4 表达式
- 进行代数运算,利用恒等式调整代码
- 削弱运算强度
- 把某些数据在编译的时候算出来
- 预先算出结果
- 删除公共子表达式