讀《編程匠藝——編寫卓越的代碼》:防御性編程

田納西.威廉斯(Tennessee Williams):
我們彼此猜忌,實(shí)屬情非得已。這是我們防止背叛的唯一武器

一、編寫優(yōu)秀代碼:

《程序設(shè)計原理》中M.A.Jackson寫道:
軟件工程師的智慧,就在于他們是否開始意識到:使程序能用和使程序正確,這兩者之間有什么樣的差別。

  1. 可用的代碼:提供常規(guī)輸入集,代碼給出常規(guī)輸出;一旦有意外輸入,代碼崩潰。
  2. 正確的代碼:對于所有輸入集,有正確的輸出;
  3. 優(yōu)秀的代碼:一定是正確的代碼;邏輯容易理解;代碼自然;容易維護(hù)。

產(chǎn)品級的代碼,面對不常見的輸入時不會崩潰,也不會出現(xiàn)錯誤的結(jié)果;同時,滿足其他要求,包括『可重入』、『線程安全』、時間約束等

二、一種防止代碼漏洞百出的手段——防御性編程

墨菲定律(Murphy's Law):
凡是可能出錯的事,準(zhǔn)會出錯。

防御性編程通過預(yù)見到(至少預(yù)先推測到)問題所在,斷定代碼中每個階段可能出現(xiàn)的問題,并做出相應(yīng)的防范措施,來防止這類意外產(chǎn)生。

三、防御性編程技巧

  1. 使用好的編碼風(fēng)格和合理的設(shè)計
    命名合理;審慎地使用括號;
    編碼前,設(shè)計好接口

  2. 避免閃電式編程
    每敲一個字,都想清楚你輸入的是什么
    進(jìn)入下一個環(huán)節(jié)之前,完成上一個代碼段的所有任務(wù)

  3. 不要相信任何人
    任何人,包括你自己,都可能把缺陷引入你的程序邏輯中
    用懷疑的眼光審視所有的輸入和所有的輸出,直到確保正確為止。
    在程序各處添加安全檢查

  4. 保持代碼簡單
    將復(fù)雜的代數(shù)運(yùn)算拆分為一些列單獨(dú)的語句,使邏輯清晰。

  5. 不要讓任何人做他們不該做的修補(bǔ)工作
    面向?qū)ο笳Z言中,將屬性設(shè)置為private,并提供public函數(shù)操作它們;
    如果變量可以聲明為函數(shù)內(nèi)局部變量,不要在文件范圍內(nèi)聲明;
    如果變量可以聲明為循環(huán)體內(nèi)局部變量,不要在函數(shù)范圍內(nèi)聲明。

  6. 編譯時開啟所有警告開關(guān)
    編譯器的警告能捕捉到許多愚蠢的編碼錯誤;

  7. 使用靜態(tài)分析工具
    編譯器只能對代碼進(jìn)行有限的靜態(tài)分析;
    靜態(tài)分析工具:C語言的lint;.NET的FxCop

  8. 使用安全的數(shù)據(jù)結(jié)構(gòu)
    最常見的安全隱患為緩沖溢出,緩沖溢出是由于不正確地使用固定大小的數(shù)據(jù)結(jié)構(gòu)而造成的;
    避免的方法:
    使用更安全的不允許破壞程序的數(shù)據(jù)結(jié)構(gòu)——使用類似C++的string類
    對不安全的數(shù)據(jù)結(jié)構(gòu)使用更安全的操作

  9. 檢查所有的返回值
    大多數(shù)難以察覺的錯誤都是因?yàn)槌绦騿T沒有檢查返回值而出現(xiàn)的

  10. 重視所有稀有資源,審慎地管理它們的獲取和釋放
    顯式地終止那些不再使用或不會被自動清除的對象的引用
    不要循環(huán)引用(A引用B,B引用A)

  11. 在聲明時對變量初始化

  12. 盡可能推遲變量的聲明
    使變量聲明的位置和使用它的位置盡量接近,從而防止干擾其他代碼
    不要在多個地方重用同一個臨時變量

  13. 使用標(biāo)準(zhǔn)化語言工具,寫標(biāo)準(zhǔn)化語言

  14. 使用好的診斷信息日志工具

  15. 審慎地使用強(qiáng)制轉(zhuǎn)換
    數(shù)據(jù)的強(qiáng)制轉(zhuǎn)換會影響代碼的可移植性

  16. 細(xì)則
    提供默認(rèn)行為:如同switch語句都帶default一樣,寫一個不帶else的if語句應(yīng)當(dāng)深思;
    遵從語言習(xí)慣
    檢查數(shù)值上下限:防止數(shù)值型變量上溢和下溢;確保每次運(yùn)算可靠穩(wěn)定(被除量不能為0)。
    正確的設(shè)置常量:盡可能把可以設(shè)置為常量的都設(shè)置為常量。

  17. 約束
    前置條件:輸入一段代碼前必須為真的條件,一般對參數(shù)作限定;
    后置條件:編寫一段代碼后必須為真的條件,一般對結(jié)果作判斷;
    不變條件:每當(dāng)程序執(zhí)行到達(dá)特定點(diǎn)(循環(huán)中、方法調(diào)用等)時為真的條件,防止邏輯錯誤;
    斷言:任何關(guān)于程序在給定位置狀態(tài)的陳述;

  18. 約束的內(nèi)容
    檢查所有的數(shù)組訪問是否都在邊界內(nèi)
    在廢棄指針之前斷言指針是非零的
    確保函數(shù)參數(shù)有效
    在函數(shù)結(jié)果返回之前對其進(jìn)行充分檢查

  19. 移除約束
    通常在程序構(gòu)建的開發(fā)和調(diào)試階段,才需要約束檢驗(yàn);確保程序邏輯正確后,理論上可以移除。
    C/C++標(biāo)準(zhǔn)庫提供了公共機(jī)制——斷言;指定為NDEBUG編譯,可移除斷言
    Java通過JVM啟動或禁用斷言機(jī)制
    .NET在框架Debug中提供斷言機(jī)制

內(nèi)容相關(guān):

可重入代碼:允許被多個進(jìn)程同時訪問和使用的一段代碼,而且無論哪個進(jìn)程調(diào)用它,所得到的結(jié)果都是一樣。為了防止某一個進(jìn)程的修改而導(dǎo)致不同進(jìn)程的結(jié)果不同,可重入代碼中一般采用局部變量不使用全局變量或靜態(tài)變量。

線程安全:如果你的代碼所在的進(jìn)程中有多個線程在同時運(yùn)行,而這些線程可能會同時運(yùn)行這段代碼。如果每次運(yùn)行結(jié)果和單線程運(yùn)行的結(jié)果是一樣的,而且其他的變量的值也和預(yù)期的是一樣的,表示你的代碼是線程安全的。

靜態(tài)分析:程序運(yùn)行前,執(zhí)行代碼檢查

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

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,502評論 19 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,623評論 18 399
  • 剛過去不久的七月,火辣辣的熱,在這份熱辣中卻也有一份略顯冰涼的愁緒。一批剛畢業(yè)的學(xué)生剛告別了校園,進(jìn)入了職場,一個...
    A_先生閱讀 292評論 0 0
  • 給家里打電話對在外拼搏的年輕人來說,是一件很尷尬的事情。心里惦記著家人,卻又要硬挺著報喜不報憂。我給家里打電話還有...
    鮑爾至金的包閱讀 610評論 0 0
  • 長風(fēng)浩蕩,拂面微醺,吹來淡淡的青草香。循著那沁人心脾的香氣遠(yuǎn)望,映入眼簾的是一汪清淺的河水啊!波光粼粼的水面,搖曳...
    安言靜語閱讀 435評論 0 0

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