《代码大全2》读书笔记 第八章 防御式编程

8 防御式编程

  • 子程序应该不因传入错误数据而被破坏,哪怕是由其他子程序产生的错误数据。

8.1 保护程序免遭非法输入数据的破坏

  • 检查所有来源于外部的数据的值。
  • 检查子程序所有输入参数的值。
  • 决定如何处理错误的输入数据。

8.2 断言(Assertions)

  • 用错误处理代码来处理预期会发生的状况,用断言来处理绝不应该发生的状况。
  • 把断言当作可执行的注释。
  • 避免把需要执行的代码放到断言中。
  • 用断言来注解并验证前条件和后条件。
  • 对于高健壮性的代码,应该先使用断言再处理错误。(仅对大型系统而言,一般程序应该使用一种一致的处理方法。)

8.3 错误处理技术(Error-Handling Techniques)

  • 返回中立值。
  • 换用下一个正确的数据。
  • 返回与前次相同的数据。
  • 换用最接近的合法值。
  • 记录日志。
  • 返回一个错误码。
  • 调用统一的错误处理子程序或对象。(整个程序会与之高耦合)
  • 当错误发生时显示出错信息。
  • 用最妥当的方式在局部出错错误。
  • 关闭程序。

  • 正确性(correctness):意味着永不返回不准确的结果,哪怕不返回数据。

  • 健壮性(robustness):意味着不断尝试采取某些措施,以保证软件可以持续地运转下去,哪怕有时做出一些不够准确地结果。

  • 确定一种通用的处理错误参数的方法,是架构层次(或称高层次)的设计决策。

8.4 异常(Exceptions)

  • 用异常通知程序的其他部分,发生了不可忽略的错误。
  • 只在真正例外的情况下才抛出异常。也就是在其他编码实践方法都无法解决的情况下才使用异常。
  • 它的应用情形和断言相似:用来处理那些不仅罕见甚至永远不该发生的情况。
  • 异常的负面:异常弱化了封装性,增加了复杂度。
  • 不能用异常来推卸责任。
  • 避免在构造函数或者析构函数抛异常,除非你在同一地方把他们捕获。
  • 在恰当的抽象层次抛出异常。异常也是程序接口的一部分。(不要抛出更低层的异常,应抛出一个与其所在类的接口相一致的异常。)
  • 在异常消息中加入关于导致异常发生时的全部信息。(如数组的上下界)
  • 避免使用空的catch语句。如果确实无法将某较低层次上的异常表现为调用方抽象层次上的异常,在采用空catch的同时也要用注释或日志对其进行文档化说明。
  • 了解所用函数库可能抛出的异常。
  • 考虑创建一个集中的异常报告机制。
  • 把项目中对异常的使用标准化。

    • 抛出哪些异常。
    • 考虑创建项目的特定异常类。
    • 规定在何种场合允许代码使用throw-catch在局部进行处理。
    • 规定何种场合允许代码抛出不在局部处理的异常。
    • 确定是否要使用集中的异常报告机制。
    • 规定是否允许在构造函数和析构函数中使用异常。
  • 考虑异常的替换方案。

8.5 隔离程序,使之包容由错误造成的损害

  • 把某些接口选定为“安全”区域的边界,对穿越安全区域边界的数据进行合法性校验,并当数据非法时做出敏锐的反映。
  • 用于类的层次:公用方法可以假设数据是不安全的,负责检查数据并清理。私用方法就假定数据是安全的了。
  • 在输入数据时将其转换为恰当的类型。
  • 隔栏外部使用错误处理技术,隔栏内部使用断言技术。

8.6 辅助调试的代码

不要自动地把产品版的限制强加于开发版之上

  • 开发中的软件允许运行缓慢,可以牺牲一些速度和对资源的使用,来换取一些可以让开发更顺畅的内置工具。

尽早引入辅助调试的代码

采用进攻式编程

  • 确保断言语句使程序终止运行。
  • 完全填充分配到的所有内存。
  • 完全填充分配到的所有文件或流。
  • 确保每一个defaultelse分支都能产生严重错误,让错误不会被忽视。
  • 在删除一个对象之前把它填满垃圾数据。
  • 让程序把它的错误日志用电子邮件发给你。

计划移除调试辅助的代码

  • 使用类似antmake这样的版本控制工具。
  • 使用内置的预处理器。
  • 编写你自己的预处理器。
  • 使用调试存根(debugging stubs)。开发版和发行版使用不同的检查程序。

8.7 确定在产品代码中该保留多少防御式代码

  • 保留那些检查重要错误的代码。确定程序的哪些部分可以承担未检测出错误而造成的后果,而哪些不能承担。
  • 去掉检查细微错误的代码。
  • 去掉可以导致程序硬性崩溃的代码。
  • 保留可以让程序稳妥地崩溃的代码。
  • 为你的技术支持人员记录错误信息。
  • 确认留在代码中的错误消息是友好的。

8.8 对防御式编程采取防御的姿态

  • 过度的防御式编程会使代码臃肿而缓慢。