防御式編程

最近業(yè)余時(shí)間在閱讀《代碼大全》,閱讀“防御式編程”章節(jié)的時(shí)候非常受啟發(fā),自己之前對(duì)系統(tǒng)的錯(cuò)誤處理這塊也確實(shí)隨意了。

什么時(shí)候應(yīng)該代碼應(yīng)該自己catch掉錯(cuò)誤并且處理了,什么時(shí)候應(yīng)該拋出給上層,拋到上層時(shí)候要如何處理,如何使用斷言等等沒(méi)有一套規(guī)范。所以這里總結(jié)下應(yīng)該如何來(lái)做防御式編程。

什么是防御式編程

防御式編程是承認(rèn)程序都會(huì)有問(wèn)題,無(wú)論是自己寫的模塊,團(tuán)隊(duì)中其他人寫的模塊甚至第三方工具包。然后依據(jù)這個(gè)來(lái)指導(dǎo)寫代碼,子程序不應(yīng)該因?yàn)閭魅氲腻e(cuò)誤數(shù)據(jù)被破壞了。

為什么需要防御式編程

因?yàn)榉婪犊此莆⑿〉腻e(cuò)誤,收獲可能大到你的想象。

最簡(jiǎn)單的例子就是對(duì)輸入的參數(shù)做校驗(yàn),比如刪除一個(gè)數(shù)據(jù)的接口需要對(duì)輸入?yún)?shù)做校驗(yàn)防止產(chǎn)生全匹配從而刪除全部數(shù)據(jù)。

如何來(lái)做

一般會(huì)結(jié)合使用斷言(asset)和錯(cuò)誤捕獲及處理(try...catch)。

斷言對(duì)于前端開發(fā)來(lái)說(shuō)可能有些陌生,斷言的語(yǔ)法是assert(equal, message),只要equal為假,那么message會(huì)被記錄下來(lái)(對(duì)于前端來(lái)說(shuō)可能是寫到console中,對(duì)于后端來(lái)說(shuō)可能是寫到日志文件)。

兩者區(qū)別在那里呢:斷言來(lái)處理絕對(duì)不應(yīng)該發(fā)生的狀況(如果發(fā)生了,說(shuō)明代碼存在bug),錯(cuò)誤處理來(lái)處理預(yù)期會(huì)發(fā)生的狀況,或者說(shuō)斷言用來(lái)檢查代碼的bug,錯(cuò)誤處理用來(lái)檢查有害的輸入數(shù)據(jù)

斷言更多是在開發(fā)中使用的,可以更好的幫助我們?cè)陂_發(fā)過(guò)程中定位錯(cuò)誤(如果console中出現(xiàn)了斷言的記錄,我們可以順著調(diào)用棧找到出錯(cuò)的源頭,這比自己猜測(cè)然后加斷點(diǎn)調(diào)試要快一些)。如果不是對(duì)性能有太大的影響的話我個(gè)人建議可以在生產(chǎn)環(huán)境也打開斷言記錄到sentry之類的日志服務(wù)中(需要注意信息的加密),這樣可以更好的幫助我們定位bug。

比如從一個(gè)用戶列表中刪除一個(gè)用戶的函數(shù)deleteUser(userId),可以使用斷言來(lái)判斷這個(gè)用戶是不是在列表中,如果沒(méi)在列表中說(shuō)明肯定是代碼出現(xiàn)了bug了(比如重復(fù)刪除同一個(gè)用戶或者這個(gè)函數(shù)的調(diào)用者傳參數(shù)存在問(wèn)題):

class User {
  constructor() {
    this.userList = [];
  }

  deleteUser(userId) {
    const isExitUser = this.userList.findIndex(user => user.id === userId) !== -1;
    asset(isExitUser, `${userId}不在用戶列表中`);
  }
}

錯(cuò)誤處理來(lái)應(yīng)對(duì)預(yù)期會(huì)發(fā)生的狀況,比如客戶端請(qǐng)求了一個(gè)api接口,正常情況下這個(gè)接口返回200,但還是存在極少數(shù)的情況下這個(gè)接口會(huì)返回500,我們可以捕捉到這個(gè)錯(cuò)誤來(lái)做一些處理,比如重新發(fā)起一次請(qǐng)求之類的。

fetchUserList().then((res) => {
  storeUserInfo(res.data);
}).catch((e) => {
  if (e.message = '500') {
  // TODO 這個(gè)函數(shù)還要注意下調(diào)用棧的深度,最多重復(fù)執(zhí)行四次,防止調(diào)用棧過(guò)深導(dǎo)致爆掉
    fetchUserList();
  }
})

異常使用有這么幾個(gè)原則:

  1. 如果異常可以在局部處理就在局部處理,不要放到外面去。

  2. 千萬(wàn)不要只捕獲異常卻什么都不做,比如fertchUserList().catch(() => {}),catch函數(shù)里面沒(méi)有做任何處理。

  3. 拋出的異常的消息中加入異常發(fā)生的相關(guān)信息,比如如果在刪除一個(gè)用戶的時(shí)候,因?yàn)橛脩舨辉诹斜碇形覓伋隽艘粋€(gè)異常,那么異常就要寫明白,要?jiǎng)h除的用戶的id是什么以及其他環(huán)境信息,這樣可以幫助我們排查問(wèn)題。

寫代碼的時(shí)候一定要去考慮要各種異常的情況并做好穩(wěn)妥的處理,這樣才能寫出更加魯棒性高并且易于debug的代碼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 防御式編程 在防御式駕駛中要建立這樣一種思維,那就是你永遠(yuǎn)也不能確定另一位司機(jī)將要做什么。這樣才能夠確保在其他人做...
    劉碩jessie閱讀 5,577評(píng)論 1 49
  • 主要思想: 子程序應(yīng)該不因傳入錯(cuò)誤數(shù)據(jù)而被破壞, 哪怕是由其他子程序產(chǎn)生的錯(cuò)誤數(shù)據(jù);換一種說(shuō)法是, 程序員應(yīng)該承認(rèn)...
    娟子閱讀 2,646評(píng)論 0 1
  • 在防御式駕駛中要建立這樣一種思維,那就是你永遠(yuǎn)也不能確定另一位司機(jī)將要做什么。這樣才能確保在其他人做出危險(xiǎn)動(dòng)作時(shí)你...
    沒(méi)故事的卓同學(xué)閱讀 3,677評(píng)論 2 15
  • 第21天·21天告別拖延 #玩卡不卡·每日一抽# 每一位都可以通過(guò)這張卡片覺察自己: 1、直覺他叫什么名字?小舒...
    陳恒麗閱讀 272評(píng)論 0 0
  • 章叁 許昕愣愣的坐在秦公館寬大的沙發(fā)上,修長(zhǎng)蒼白的手指有一下沒(méi)一下的敲打著著沙發(fā)的扶手,不顧旁邊樊振東低聲勸慰著“...
    秦北誠(chéng)閱讀 668評(píng)論 0 0

友情鏈接更多精彩內(nèi)容