事情的來由還要從幾十幾億年前的一次星球大爆炸提及,sorry,背錯臺詞了,是從幾天前討論接口返回數據和幾個月前討論課件本地數據結構提及,簡單的說,就是碰到約定好的內容出現異常,是咱們在程序中內部做兼容處理,仍是拋出去。前端
打個比方,咱們要解析一段json,約定這個json的格式,只能是正常格式,或者是空,那麼一旦返回json的方法返回了一個『既不是正常格式,又不是空的異常值』,程序該如何處理呢?編程
小花:一旦碰到約定異常,程序必須兼容處理,必定不能讓程序Crash
小Fa:一旦碰到約定異常,就必須拋出去,告知約定有誤,找出具體錯誤緣由json
這個問題,相信只要是程序猿基本都遇到過,舉個最多見的栗子,NullPointerException,假如咱們要從json中取一個字段,忽然發現發生了NullPointerException,一些開發者認爲是數據問題,那麼把json中的這個字段改正確就好了;還有一些開發者認爲是程序問題,認爲程序須要作非空判斷,再去使用。我相信這兩種程序猿都有本身的理由,第一種程序簡潔明瞭,代碼邏輯乾淨,但一旦出錯,就會崩潰,第二種程序耐操,隨你數據怎麼錯,我都能不Crash,但代碼中處處存在非空判斷,臃腫、重複。後端
生存仍是毀滅,這是一個問題!安全
就在咱們爲了這個問題而爭論的時候,忽然有一個姓康的同事,施法祭出了一塊磚頭(《代碼大全2》,近900頁,至關於3本《Android羣英傳》),我一度覺得他想砸在個人臉上,正當我準備閃避的時候,他翻到了這塊磚頭的第八章,幾個大字赫然印入了個人視線——『防護式編程』。數據結構
果真是老司機,竟然能夠從防護性駕駛中悟出防護性編程,說好的編程不開車,開車不編程呢?編程語言
這位做者編程厲不厲害我不知道,但我知道,論開車,必定沒有何老師diao!ide
OK,《代碼大全》給咱們提供了一個定義——『防護式編程』,說白了,就是『人類都是不安全、不值得信任的,全部的人,都會犯錯誤,而你寫的代碼,應該考慮到全部可能發生的錯誤,讓你的程序不會由於他人的錯誤而發生錯誤』函數
在書中,做者告訴咱們,程序須要對可能的錯誤輸入,作出兼容,例如一個除法的函數,你必須判斷分母可能爲0的狀況,從而給調用者返回錯誤提示。另外,通常的高級編程語言,都提供了『斷言』和『異常』兩種方式來進行錯誤處理。編碼
斷言,是一種在開發階段使用的,讓程序在運行時進行自檢的代碼,斷言爲真,那麼程序運行正常,斷言爲假,那麼程序運行異常退出。等等,防護式編程不是說好的要兼容異常嗎,爲何會退出?實際上,做者的意思是,先斷言、後處理錯誤,而斷言是在開發環境中的,正式上線後是不會有斷言的。
但實際上,這是一個悖論,開發階段的錯誤處理代碼在開發階段被斷言給攔截掉了,但錯誤處理代碼也是人寫的,那麼如何去檢測『錯誤處理代碼可能發生的錯誤』呢?
當代碼出現問題時,能夠經過拋出異常來進行通知,若是你沒法處理,則能夠交給外界進行處理。這個很少說,畢竟大部分代碼,若是有異常,最簡單的就是try catch了,我甚至見過把因此代碼直接try catch的,你是有多不相信人類。
因此我以爲防護式編程用久了,會不會開始懷疑人生,果真,在日後翻幾頁,做者也給出了建議。
借用奇異博士的一句臺詞——『你TM竟然把警告寫在咒語的下一頁』!
簡而言之,防護式編程,就是持懷疑態度審視全部的代碼,但這個和咱們討論的主題仍是略有不一樣的,咱們討論的主題是『已經有了約定,但返回了約定以外的內容』。
就在咱們討論的時候,天空忽然飄來五個字——那都不是事,哦不對,是『契約式編程』。
這個好像有點像!咱們先來簡單的看下什麼是契約式編程,簡單的說,契約做用於兩方,每一方都會完成一些任務,從而促成契約的達成,但同時,每一方也會接受一些義務,做爲制定契約的前提,有任意一方無視了必盡義的義務,則契約失敗。
契約式編程要求咱們在『前提條件』、『後繼條件』和『不變量條件』進行契約的檢查。相似的,例如檢查參數,一旦參數不對,立即撕毀契約。這一點,如今不少新的語言都支持了,例如Swift,就支持對參數進行約束檢查,這就是一種類契約式編程。
契約所約束的,是『一個爲了確保程序正常運行的條件』,一旦契約被損毀,只有一個緣由,那就是程序出了Bug,例如一個數據字段,在我處理的時候,必須保證是不爲空的,那麼誰來保證這一點呢,必定是個人調用方(或者說是其它模塊),因此,一旦出現問題,應該有調用方來檢查,確保調用的時候,必須是不爲空的。
這讓我想到了剛開始在面向日本人編程時期的一些事,日本人的作事風格是出了名的謹慎和詳細,每個方法、函數,在詳細設計的時候,就已經把參數、返回值,已經它們的類型和全部可能的值都設計好了,每一個方法之間有着明確的界限,若是你的方法由於傳入的參數不在設計範圍內而致使錯誤,你徹底能夠去找調用方,要求他按照設計來進行調用。不得不說,這應該是契約編程的最佳實踐。日企廣泛使用這種方式其實還有一個緣由,那就是能夠嚴格區分責任,讓每一個人都沒必要爲了遷就他人的錯誤而進行『艱難的編碼』。每一個人按照契約處理好本身的事情,讓損毀契約的人承擔責任。
再引伸一下,這和如今的『面向接口編程』也很是相似,兩個模塊之間,定義好調用、處理的接口,而具體的實現,對方都不用關心,只要安裝協議的接口來進行開發就能夠了,但光有接口也不夠,還須要契約來作進一步的約束,例如參數、返回值的約束。
無獨有偶,在《代碼大全》中,做者也提出了『進攻式編程』,其實和契約編程,有殊途同歸之妙。
OK,夢醒了,讓陽光照進現實。以上兩種編程方式,都是很是理想化的編程,但在通常的公司裏面不管是防護仍是契約,實現起來都是比較困難的,例如前端與後端的接口、不一樣部門同事的交流,按照契約式編程,沒人Care你的契約,按照防護式編程,代碼慘不忍睹,還容易漏掉防護。那麼到底該怎麼辦呢,我認爲,若是能在公司層面推廣契約式編程,首先是對開發效率的提高,讓每一個人都對本身寫的代碼負責,在開發者之間創建良好的信任關係,同時也能減小沒必要要的溝通成本和精力。但同時,必要的防護式編程也是不能少的,這是保證程序健壯、穩定的前提。怎麼說呢,中國人民秉承了千百年的傳統——『中庸之道』,契約仍是防護,視狀況而定,這是平衡的藝術。
本文請使用防護式閱讀,每一個人都會犯錯,歡迎留言交流。
此文一出,頗有可能引起雙方混戰,紅與黑,天災仍是近衛,聯盟仍是部落,Choose your side。
歡迎關注個人公衆號: