《编程匠艺》读书笔记之四

第六章 人非圣贤

  • 错误可能而且必将发生。几乎任何操作都会带来意想不到的结果,这种结果与有缺陷的程序中的bug不一样,因为你预先就知道错误会发生。
  • 如果你不编写代码来处理这些错误情况,那么你几乎肯定最终会遇到bug;你的程序不会总按你的意愿执行。

    错误产生得原因可以分为以下三种:
  • 用户错误。一个好的程序会指出错误所在,并帮助用户来改正它。
  • 程序员错误。由程序员引入的代码缺陷。
  • 意外情况。
  • 我们需要一个定义良好的策略来应对我们代码中的各种错误。不论是由人来选择怎么处理问题,还是由代码来决定——一定要由某人或者代码来负责确认错误和对错误作出反应。
  • 错误由下层组件提出并向上传播,以便让调用方来处理。

    错误以什么方式提出,称为错误报告机制,可以分为以下几种:
  • 不报告。这是最简单的情况。绝对不要忽视任何一种情况,如果你不知道如何处理这个问题,就向调用代码发送一个故障信号,不要对错误心存侥幸。
  • 返回值。从函数返回一个表示成功或者失败的值,一个更高级的方法是列举出所有可能的退出状态,并返回一个相应的原因代码,一个值表示成功,其余的值分别代表许多不同的异常终止信息。
  • 错误状态变量。这种方法试图解决函数的返回值和它的错误状态报告之间的矛盾。函数将设置一个共享的全局错误变量,而不是返回一个原因代码。但是这种方法在多线程时会很麻烦。从功能上讲,这种技术与使用返回值是等价的,但是它的缺陷之多足够让你放弃使用。
  • 异常。异常是一种管理错误的语言工具;不是所有的语言都支持异常。异常是不能被忽略的,如果没有被捕获和处理,异常就会传播到调用堆栈的最高层,这通常会导致程序崩溃,这使得异常相对于手工编写的错误处理代码来说更加整洁和安全。处理一个异常的代码与引发该异常的代码截然不同,而且可以差的很远;异常并非没有开销,语言对异常的支持会造成性能上的损失。
  • 信号。它是一种更加极端的报告机制,主要用于由执行环境向运行程序报告错误,信号是硬件中断的软件等价物。
  • 有恢复能力的代码一定是针对异常安全的,不论出现什么异常,它都必须正确的运行。
  • 异常中立的代码将所有的异常都传播到调用方,它不会隐藏或者改变任何东西。
  • 异常安全分为以下3个等级:1. 基本保证,如果异常在一个函数中发生,它将不会泄露资源,代码的状态将保持连贯,但是它不一定会保持一种已知的状态;2. 强力保证,如果通过你的代码传播了一个异常,程序的状态将保持完全不变;3. 不抛出保证,操作永远不能抛出异常,如果我们是异常中立的,那么就意味着函数不能执行任何可能会抛出异常的操作。

    根据不同的错误报告机制,我们有不同的检测错误的方式:
  • 返回值。
  • 错误状态变量。
  • 异常。只有在知道可能会抛出异常的情况下,你才能作出明智的选择,而只有这已被文档化的情况下,你才能够知晓。
  • 信号。需要为其安装一个处理程序。


    在对一个错误进行处理前,我们需要了解错误相关的一些关键信息:
  • 错误来自何处。
  • 当时的上下文环境。
  • 为什么会出错。
  • 错误是何时发生的。
  • 错误的严重性。
  • 如何修正错误。

  • 我们在何时处理错误,可以分成两派:1. 尽可能早的处理,因为错误是在其来源附近处理的,所以你可以留住重要的上下文信息,从而使得你的代码更加清晰,对于返回错误代码的函数来说通常是最佳的选择;2. 尽可能推迟处理,因为检测到错误的代码很少知道应该怎样进行处理,如何处理通常取决于错误出现的环境,即调用环境,异常对于这种处理方式很适合。
  • 我们在修正错误时可能会用到的技巧:1. 日志,日志的作用是记录程序的生命周期中值得注意的事件,以使你可以深入研究程序内部工作方式和执行的重构路径;2. 报告,程序应该只在已没有什么可作的情况下才向用户报告错误;3. 传播,如果你的代码遇到了一个错误并且不知道该怎么办,那么就把错误向上传,很可能你的调用方有能力解决这个问题;4. 忽略,如果采取这种方法,必须在代码中明显的标记出来。

    如果你要提示用户来解决问题,那么首先应该在头脑中有一些总体的思路:
  • 用户并不像程序员那么思考,所以以他们所期望的方式来呈现信息。
  • 确保你的信息不显得高深莫测。
  • 不要呈现毫无意义的错误代码。
  • 将后果严重的错误与仅仅是警告区分开。
  • 只在用户完全理解每种选择的后果时在提出问题,如果需要的话,对问题进行一些解释,使每种选择的后果都非常清晰。

    每个函数中都应该执行的一些错误检查:
  • 检查所有的函数参数。
  • 检查执行过程中关键点处的不变条件是否满足。
  • 在使用任何来自外部的值之前都要检查其有效性。
  • 检查所有系统调用和其他下级函数调用的返回状态。

  • 在编写可能会失败的代码的同时,编写所有的错误检测和处理,不要拖延到以后做,如果你很不幸必须推迟处理,至少现在就编写错误检测部分。
  • 既考虑错误的引发又考虑错误的处理的一般原则是无论故障在什么情况下出现,对于故障的处理都有一个一致的策略。
  • 你所编写的每行代码都必须在恰当和彻底的错误检查与处理上取得平衡。
  • 我们应该如何处理在错误处理代码中发生的错误?应该像对待其他任何错误一样,处理那些在错误处理程序中发生的错误,你会得到一个多层嵌套的错误处理程序
  • 优秀的程序员:1. 将好的意图与好的编码习惯结合在一起;2. 在编写主要代码的同时编写错误处理代码;3. 在他们编写的代码中全面覆盖了所有的错误可能性。
  • 糟糕的程序员:1. 编写代码时很随便,既不思考也不检查他们在做什么;2. 忽略在编写代码时出现的错误;3. 最终不得不进行漫长的调试过程以找到程序崩溃的原因,因为他们从不预先考虑错误情况。
posted @ 2008-10-19 21:21  李潘  阅读(432)  评论(0编辑  收藏  举报