作者:HW-HM
上一篇文章【码手防御-2】契约式设计原则,我们讨论了契约式设计六大原则,本篇将介绍防御式编程相关的技术。本篇只是一个列表,后续将针对典型的技术和场景展开讨论。
1. 使用良好的编码风格和可靠的设计。少用花哨而复杂的算法,简单直接的设计才是可靠的设计。
2. 别匆匆忙忙编码。越是紧急的时候,越是要放慢,编码时仔细思考你正在输入什么。
3. 谁也别信,使用断言确认假定,对外部输入的各种情况谨慎处理。真实用户、恶意用户、客户端代码、运行环境等以及你自己都可能给你的程序引入缺陷,对所有的输入和结果都保持怀疑除非你已经验证了它们的有效性。
4. 编写清晰的代码,而不是紧凑的代码。简单就是美,别把代码整得过于复杂,而且看起来紧凑的代码未必就是高效的。
5. 别让别人看到他不该看的、使用他不该用的。内部的东西就应该放在里面,别让外面的人看见,类成员变量应该是私有的,成员函数也是一样,变量的作用域越小越好。如果你把实现的内部东西暴露了,别人就会用,别人用了之后你想改都改不了。
6. 编译时把所有的告警选项都打开。这能发现很多愚蠢的编码错误,总是打开它,并且清除掉所有的告警。
7. 使用静态分析工具。编译器的检查还是很有限,可以使用更强的静态检查工具进行进一步检查,例如PC Lint。
8. 使用安全的数据结构和方法。例如使用strncpy而不是strcpy。
9. 检查每一个函数返回值。如果函数有返回值,必然有它的理由,如果返回值是一个错误码,你必须检查并妥善处理。对于可能抛出异常的函数,也要进行异常捕获并妥善处理。
10. 小心处理内存和其他宝贵资源。别忘了释放内存和关闭文件,尽量遵从资源谁申请,谁负责释放的原则。
11. 在定义变量时进行初始化。
12. 在真正需要使用时才定义变量。别在一开始定义一大堆变量,否则到后面使用时鬼知道前面初始化的值是多少。需要时才定义,代码会更清晰,而且更不容易出错。
13. 使用标准的语言特性。语言规范中没有定义的东西尽量别用,否则移植到另一个环境就出问题了。就算不移植,以后人家编译器升级后变了,可能就嗝屁了。
14. 使用一套便于诊断问题的日志工具。
15. 小心地进行类型转换。尽量避免编译器进行自动的类型转换,避免将有符号和无符号类型比较。
16. 为switch语句提供default。
17. 遵从语言惯例。
18. 检查数字的界限。例如检查除数是否为0。
19. 尽量const化保持正确性。C++的const修饰符,不仅是一种编码规则说明,而且还能够让编译器发现很多愚蠢的编程错误。
20. 从程序中删去无定义的特性或者在程序中使用断言来检查出无定义特性的非法使用。
21. 利用断言来检查不可能发生的情况。
22. 要利用不同的算法对程序的结果进行确认。使用最简单的实现来验证复杂实现就是一个好办法,例如用冒泡排序的结果来验证快速排序实现的正确性。
23. 对新代码进行逐条跟踪,并密切观察数据流。对新写的函数做这件事情通常能发现很多浅显的、隐藏的错误。对于无法做单元测试的代码这一点尤其重要。
24. 一旦发现错误,就补充对应的测试。
25. 编写功能单一的函数。不要编写多种功能集于一身的函数,将查询(只读)和命令(改写)分开成不同的函数。复杂的东西总是容易出错。
26. 不能毫无必要地将不用类型地操作符混合使用,如果必须将不同类型地操作符混合使用,就用括号把它们隔离开来。
27. 不要利用静态(或全局)量存储区传递数据。多次调用会破坏之前的数据,而且多线程环境中很不安全。
28. 为一般水平的程序员编写代码。就算你单独终身维护自己的代码,也不为过,写得简单易懂,你自己看起来也容易。
29. 尽量编写和测试小块代码。写一万行代码,然后进行测试,鬼知道以前怎么实现的,天知道会有多少错误。你应该在稳固的基础上再构建稳固的东西。
30. 明确定义函数参数和返回值。避免在正常地返回值中隐藏错误代码,例如getItemIndex,返回正数表示对应的索引,返回负数表示对应的错误码。避免使用bool参数,bool参数往往意谓着两部分逻辑混合到了一起,而在调用点很难看出调用的是那种逻辑。
31. 编写函数使其在给定有效的输入情况下不会失败,也就是说要尽量编写始终满足契约的函数。对于支持异常的语言,不能满足契约时,通过抛出异常的方式于正常的逻辑区分开来。






































苏公安网备 32011402010933号