契約式程式設計與防禦式程式設計

2021-08-18 03:15:20 字數 2787 閱讀 3320

事情的來由還要從幾十幾億年前的一次星球大**說起,sorry,背錯台詞了,是從幾天前討論介面返回資料和幾個月前討論課件本地資料結構說起,簡單的說,就是碰到約定好的內容出現異常,是我們在程式中內部作相容處理,還是丟擲去。

打個比方,我們要解析一段json,約定這個json的格式,只能是正常格式,或者是空,那麼一旦返回json的方法返回了乙個『既不是正常格式,又不是空的異常值』,程式該如何處理呢?

小花:一旦碰到約定異常,程式必須相容處理,一定不能讓程式crash

小fa:一旦碰到約定異常,就必須丟擲去,告知約定有誤,找出具體錯誤原因

這個問題,相信只要是程式猿基本都遇到過,舉個最常見的栗子,nullpointerexception,假如我們要從json中取乙個字段,突然發現發生了nullpointerexception,一些開發者認為是資料問題,那麼把json中的這個欄位改正確就行了;還有一些開發者認為是程式問題,認為程式需要做非空判斷,再去使用。我相信這兩種程式猿都有自己的理由,第一種程式簡潔明瞭,**邏輯乾淨,但一旦出錯,就會崩潰,第二種程式耐操,隨你資料怎麼錯,我都能不crash,但**中到處存在非空判斷,臃腫、重複。

生存還是毀滅,這是乙個問題!

就在我們為了這個問題而爭論的時候,突然有乙個姓康的同事,施法祭出了一塊磚頭(《**大全2》,近900頁,相當於3本《android群英傳》),我一度以為他想砸在我的臉上,正當我準備閃避的時候,他翻到了這塊磚頭的第八章,幾個大字赫然印入了我的視線——『防禦式程式設計』。

果然是老司機,居然可以從防禦性駕駛中悟出防禦性程式設計,說好的程式設計不開車,開車不程式設計呢?

這位作者程式設計厲不厲害我不知道,但我知道,論開車,一定沒有何老師diao!

ok,《**大全》給我們提供了乙個定義——『防禦式程式設計』,說白了,就是『人類都是不安全、不值得信任的,所有的人,都會犯錯誤,而你寫的**,應該考慮到所有可能發生的錯誤,讓你的程式不會因為他人的錯誤而發生錯誤』

在書中,作者告訴我們,程式需要對可能的錯誤輸入,做出相容,例如乙個除法的函式,你必須判斷分母可能為0的情況,從而給呼叫者返回錯誤提示。另外,一般的高階程式語言,都提供了『斷言』和『異常』兩種方式來進行錯誤處理。

斷言,是一種在開發階段使用的,讓程式在執行時進行自檢的**,斷言為真,那麼程式執行正常,斷言為假,那麼程式執行異常退出。等等,防禦式程式設計不是說好的要相容異常嗎,為什麼會退出?實際上,作者的意思是,先斷言、後處理錯誤,而斷言是在開發環境中的,正式上線後是不會有斷言的。

但實際上,這是乙個悖論,開發階段的錯誤處理**在開發階段被斷言給攔截掉了,但錯誤處理**也是人寫的,那麼如何去檢測『錯誤處理**可能發生的錯誤』呢?

當**出現問題時,可以通過丟擲異常來進行通知,如果你無法處理,則可以交給外界進行處理。這個不多說,畢竟大部分**,如果有異常,最簡單的就是try catch了,我甚至見過把所以**直接try catch的,你是有多不相信人類。

所以我覺得防禦式程式設計用久了,會不會開始懷疑人生,果然,在往後翻幾頁,作者也給出了建議。

簡而言之,防禦式程式設計,就是持懷疑態度審視所有的**,但這個和我們討論的主題還是略有不同的,我們討論的主題是『已經有了約定,但返回了約定之外的內容』。

就在我們討論的時候,天空突然飄來五個字——那都不是事,哦不對,是『契約式程式設計』。

這個好像有點像!我們先來簡單的看下什麼是契約式程式設計,簡單的說,契約作用於兩方,每一方都會完成一些任務,從而促成契約的達成,但同時,每一方也會接受一些義務,作為制定契約的前提,有任意一方無視了必盡義的義務,則契約失敗。

契約式程式設計要求我們在『前提條件』、『後繼條件』和『不變數條件』進行契約的檢查。類似的,例如檢查引數,一旦引數不對,當即撕毀契約。這一點,現在很多新的語言都支援了,例如swift,就支援對引數進行約束檢查,這就是一種類契約式程式設計。

契約所約束的,是『乙個為了確保程式正常執行的條件』,一旦契約被損毀,只有乙個原因,那就是程式出了bug,例如乙個資料字段,在我處理的時候,必須保證是不為空的,那麼誰來保證這一點呢,一定是我的呼叫方(或者說是其它模組),所以,一旦出現問題,應該有呼叫方來檢查,確保呼叫的時候,必須是不為空的。

這讓我想到了剛開始在面向日本人程式設計時期的一些事,日本人的做事風格是出了名的謹慎和詳細,每乙個方法、函式,在詳細設計的時候,就已經把引數、返回值,已經它們的型別和所有可能的值都設計好了,每個方法之間有著明確的界限,如果你的方法因為傳入的引數不在設計範圍內而導致錯誤,你完全可以去找呼叫方,要求他按照設計來進行呼叫。不得不說,這應該是契約程式設計的最佳實踐。日企普遍使用這種方式其實還有乙個原因,那就是可以嚴格區分責任,讓每個人都不必為了遷就他人的錯誤而進行『艱難的編碼』。每個人按照契約處理好自己的事情,讓損毀契約的人承擔責任。

再引申一下,這和現在的『面向介面程式設計』也非常類似,兩個模組之間,定義好呼叫、處理的介面,而具體的實現,對方都不用關心,只要安裝協議的介面來進行開發就可以了,但光有介面也不夠,還需要契約來做進一步的約束,例如引數、返回值的約束。

無獨有偶,在《**大全》中,作者也提出了『進攻式程式設計』,其實和契約程式設計,有異曲同工之妙。

ok,夢醒了,讓陽光照進現實。以上兩種程式設計方式,都是非常理想化的程式設計,但在一般的公司裡面不論是防禦還是契約,實現起來都是比較困難的,例如前端與後端的介面、不同部門同事的交流,按照契約式程式設計,沒人care你的契約,按照防禦式程式設計,**慘不忍睹,還容易漏掉防禦。那麼到底該怎麼辦呢,我認為,如果能在公司層面推廣契約式程式設計,首先是對開發效率的提公升,讓每個人都對自己寫的**負責,在開發者之間建立良好的信任關係,同時也能減少不必要的溝通成本和精力。但同時,必要的防禦式程式設計也是不能少的,這是保證程式健壯、穩定的前提。怎麼說呢,中國人民秉承了千百年的傳統——『中庸之道』,契約還是防禦,視情況而定,這是平衡的藝術。

此文一出,很有可能引發雙方混戰,紅與黑,天災還是近衛,聯盟還是部落,choose your side。

契約式程式設計與防禦式程式設計

事情的來由還要從幾十幾億年前的一次星球大 說起,sorry,背錯台詞了,是從幾天前討論介面返回資料和幾個月前討論課件本地資料結構說起,簡單的說,就是碰到約定好的內容出現異常,是我們在程式中內部作相容處理,還是丟擲去。打個比方,我們要解析一段json,約定這個json的格式,只能是正常格式,或者是空,...

契約式程式設計

契約是減少大型專案成本的突破性技術。契約由先驗條件 後驗條件 錯誤和不變數等概念組成。契約可以而加到 c 中而無需對語言加以改造,但是卻十分笨拙且不一致。在語言內部支援契約的目的是 給契約乙個一致的觀感 提供工具支援 使編譯器能夠根據從契約中收集的資訊生成更好的 易於管理並強制實行契約 處理契約繼承...

Design by Contract(契約式設計)

design by contract 是bertrand meyer 總結的一項設計技巧 design by contract 的核心是斷言 assertion 所謂 斷言 是指永遠為真的布林型語句,如果不為真,則程式必然存在錯誤。通常情況下,檢查斷言的時機,應該侷限於除錯 debug 階段,而不是...