异常处理的设计原则

正常优先

以 Go 语言的函数ReadFile为例。定义以下:javascript

func ReadFile(filename string) ([]byte, error)

这个函数会将error返回。事实上,大部分 Go 语言 API 都有将error返回的特征。(推荐的)调用方式以下:前端

//...
data, err = ReadFile("example.dat");
if (! err) {
    //...
}
//...

一些编程语言的 API,会提醒使用者“异常可能会出现”,暗示使用者“立刻去作异常处理”。然而异常处理的设计过程会中断正常逻辑思路。若是异常处理链过长,回归到正常逻辑的时候可能一脸懵逼。java

所以,异常处理的首要原则是“正常优先”。也就是说,若是代码写到这里才发现“可能须要设计异常处理”(即异常处理不在原有的设计预期之中),那就无视之(最多留个注释),优先完成正常逻辑。编程

这里的“异常设计”是广义的,并不限定在try {...} catch (exception) {...}的形式上。准确来讲,“异常设计”它还包含了“容错设计”。如下列 JavaSript 代码为例:服务器

// add.js
export default function add (a, b) {
  return a + b;
}

这个函数的做用是计算两个数的和。请注意:JavaScript 不会验证参数和返回值的类型。因此,若是这个函数是个 API,调用者可能会传入类型不正确的参数,获得不正确的结果。例如:网络

// index.js
import add from './add.js';

console.log(add('3' + 4)); // 34

具有“异常设计”的代码是:编程语言

// add.js
export default function add (a, b) {
  if (typeof(a) !== 'number' || typeof(b) !== 'number') {
    throw 'Error: parameters must be number.'
  }
  return a + b;
}

例子中的if (condition) {...}严格来讲属于“容错设计”,本文中将其归类为“异常设计”(调用参数异常),也应当遵照上文说起的“正常优先”原则。函数

自用忽略

异常设计的第2个原则是“自用忽略”。也就是说,若是开发者设计 API 只会被本身调用,则不要设计异常处理。fetch

为自用 API 设计异常处理有两大弊端:设计

  • 浪费时间。开发者调用本身设计的 API,一般思路明确,不会产生错误。即便设计了异常处理,也几乎不可能被执行。设计过程自己就是在浪费时间。
  • 可能会屏蔽 IDE 的异常提示,增长调试难度。如今的 IDE 都具备强大的错误追踪功能,有的甚至能精肯定位到源文件的行。设计不当的异常处理,会影响 IDE 的异常捕获,反而下降了开发效率。

若是自用 API 将要被发布,可能会被其余开发者使用,这时就须要考虑异常处理的问题了。

量力而行

异常设计的第3个原则是“量力而行”。也就是说,对因信息缺失或环境限制而没法处理的异常,或抛出(throw)或终止(terminate)或无视(ignore)。

例如:

  • 抛出:上文的add
  • 终止:程序执行时,首先从指定位置读取配置文件,则读取失败(文件不存在、IO设备异常等),则直接终止程序。
  • 无视:这个在Web前端中十分常见,若 fetch API 失败(本地网络切换、服务器访问量过大等),则能够无视之。由于用户能够自行刷新网页解决这类问题。
相关文章
相关标签/搜索