異常處理的設計原則

正常優先

以 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 失敗(本地網絡切換、服務器訪問量過大等),則能夠無視之。由於用戶能夠自行刷新網頁解決這類問題。
相關文章
相關標籤/搜索