经验之谈
编程前
- 阅读说明, 了解要求
- 考虑 可能输入 和 期望输出
- 识别 有效但非期望的输入 以及 正确的输出
- 识别 无效输入, 并找到检测办法, 即使输入无效, 程序也不能崩溃
- 想好解决措施, 在纸上写出方法
- 画出框图, 显示信息如何在程序不同部分之间进行交换
- 有了设计后, 计划好实现层面
- 建议: 每个函数只做一件事, 且不应太长, 最好控制在一页电脑屏幕就写完
比如排序程序, 如果输入是已排序的, 该如何处理? 相同的数字多次出现, 如何处理? ...
创建多少个函数? 每个函数做什么? 需要多少个文件? ...
编程中
- 把代码提交到一个更大的程序之前, 写一个小程序, 测试你的代码, 这叫 单元测试
- 运行测试前, 一行一行读自己写的代码, 在脑中运行一些简单的测试例子
- 在继续之前, 写一些代码, 测试某些前置条件是否已满足
- 避免复制粘贴代码, 创建函数来重构代码
- 使用版本控制
- 解决所有的编译警告
如果不能保证一个函数正常工作, 那么, 把多个函数组合在一起还能正常工作就无从谈起.
完成了程序的一部分, 在继续下去之前, 要确保写好的部分是可以正常工作的.
比如, 假设排序是程序的一部分, 在程序做其他事之前, 先检查一下数据是否已保存好.
会有这样的情况, 昨天还正常, 做了些改动就无法工作了, 而改动太多, 改了哪里完全记不清了.
版本控制可以让你知道与之前版本相比的改动.
一些人会忽略警告, 认为可以在程序运行之后再解决, 然而警告常常预示一些导致程序难以运行的问题.
编程后
- 读一读程序, 检查有无常见错误, 不要依赖测试
- 未初始化变量
- 错误的数组下标
- 错误的数据类型
- 生产代码 与 测试代码 分离
- /*
- 函数内部调用测试代码
- 生产代码与测试代码混在一起, 想要移除很困难
- */
- func(args)
- {
- /* 运行, 得到结果 */
- /* 测试, 检验结果 */
- }
- /*
- 函数外部调用测试代码
- 生产代码与测试代码分离, 想要移除很简单
- */
- func(args)
- {
- /* 程序代码 */
- }
- test_func(args)
- {
- /* 创建 参数 */
- res = func(参数);
- /* 检验 res */
- }
测试只会告诉你程序没有正常工作, 无法告诉你程序真的在正常工作, 测试用例无法覆盖所有可能.
几个常见错误:
应该编写检测 bug 的程序.
比如, 要检查一个数组是否排序好, 不要打印然后靠眼睛观察, 应该写个函数.
应该在编程前就考虑编写测试代码, 这叫 测试驱动开发.
func 是被测函数, test_func 是测试 func 的代码.