代码大全2——防御式编程与调试——为「一定会出错」而设计
核心观点
McConnell 的一个根本信念:程序里一定会有 bug,输入一定会不合法,外部依赖一定会出问题。与其假装世界完美,不如把「防御」作为默认的编码姿态。
关键要点
- 防御式编程(Defensive Programming)
核心思想:垃圾进,什么都不出(Garbage in, nothing out),而不是「垃圾进,垃圾出」。
具体手段:
断言(Assertions):用来捕获「本不该发生」的程序内部错误,帮助在开发期暴露问题。
检查所有外部输入:不信任任何来自函数外、系统外、用户的数据,一律先校验。
处理所有异常路径:错误处理不是「锦上添花」,而是主流程的一部分。
2. 异常 vs 断言的分工
断言用于程序员的错误(逻辑 bug),应在测试/开发期触发。
异常/错误处理用于可预期的运行时状况(文件不存在、网络超时),应作为正常流程的一部分处理。
混淆两者,是很多错误处理混乱的根源。
3. 调试的科学方法
调试不是「瞎试」,而是提出假设 → 设计实验 → 验证假设的循环。
步骤:
先理解问题:稳定地复现 bug,弄清楚「期望行为」和「实际行为」的差异。
定位:用二分法缩小范围,从「一定没错」的代码开始排除。
修复根因:修 bug 而非修症状,同时警惕「只改了一处,别处同类问题还在」。
回归验证:确认修复没有引入新问题。
4. 代码审查与「橡皮鸭」
McConnell 强调,很多 bug 不是靠「写」消除的,而是靠「读」和「讲」消除的。
向别人(甚至一只橡皮鸭)逐行解释代码,往往会在「说出口的瞬间」发现问题。
团队层面的代码审查(Code Review)是性价比最高的缺陷预防手段之一。
5. 重构是常态
防御式编程的另一面是:承认代码不会一次写对。
把重构视为日常动作,而非「出问题了才做的事」——持续保持代码处于「当下最清晰的形态」。
我的思考
这一篇改变了我对「错误处理」的认知:它不是代码的附加部分,而是设计的核心部分。
我印象最深的是对「断言」和「异常」的区分——在此之前我常混用,导致要么该报的错没报(用了会被优化掉的断言),要么把内部 bug 当成业务错误吞掉。分清「谁的错误、何时发生、如何处理」之后,错误处理变得有条理多了。
一句话总结全书:代码是写给人读的,只是顺便让机器执行。 这三篇笔记——命名、控制结构、防御与调试——串起来,正是这条主线在不同维度的展开。
浙公网安备 33010602011771号