如何實現(xiàn)防御式編程?針對這個問題,今天小編總結(jié)這篇有關(guān)防御式編程的文章,希望能幫助更多想解決這個問題的朋友找到更加簡單易行的辦法。
創(chuàng)新互聯(lián)公司堅持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都網(wǎng)站設(shè)計、網(wǎng)站制作、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時代的六合網(wǎng)站設(shè)計、移動媒體設(shè)計的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!防御式編程的全部重點就在于防御那些你未曾預(yù)料到的錯誤。
防御式編程的主要思想:子程序應(yīng)該不因傳入錯誤數(shù)據(jù)而被破壞,哪怕是由其他子程序產(chǎn)生錯誤數(shù)據(jù)。更一般地說,其核心想法是要承認(rèn)程序都會有問題,都需要被修改,聰明的程序員應(yīng)該根據(jù)這一點來編程序。
一、保護(hù)程序免遭非法輸入數(shù)據(jù)的破壞
對已形成產(chǎn)品的軟件而言:不管進(jìn)來的數(shù)據(jù)如何,都不應(yīng)該產(chǎn)程垃圾數(shù)據(jù)。(必要的錯誤提示)
通常有三種方法來處理進(jìn)來的垃圾數(shù)據(jù):
1、檢查所有來源于外部的數(shù)據(jù)的值
當(dāng)從文件、用戶、網(wǎng)絡(luò)或其他外部接口中獲取數(shù)據(jù)時,應(yīng)檢查所獲得的數(shù)據(jù)值,以確保它在允許的范圍內(nèi)。
2、檢查子程序所有輸入?yún)?shù)的值
數(shù)據(jù)來源于其他子程序,而不是外部接口。
3、決定如何處理錯誤的輸入數(shù)據(jù)
一旦檢測到非法數(shù)據(jù),就應(yīng)該處理它。
注:防范看似微小的錯誤,收獲可能遠(yuǎn)遠(yuǎn)超出你的想象。
二、斷言(Assertions)
斷言定義:指在開發(fā)期間使用的,讓程序在運行時進(jìn)行自檢的代碼(通??梢允亲映绦蚧蚝辏#▽Υ笮偷膹?fù)雜程序或可靠性要求極高的程序來說尤其重要)
Java斷言:兩個參數(shù)—>assert(“bool表達(dá)式”,“當(dāng)判斷條件為false時的錯誤信息”)
斷言檢查如下這類假定:
1、 輸入?yún)?shù)和輸出參數(shù)的取值處于預(yù)期的范圍內(nèi)。
2、 子程序開始(或結(jié)束)執(zhí)行時,文件或流是處于打開(或關(guān)閉)的狀態(tài)。
3、 子程序開始(或結(jié)束)執(zhí)行時,文件或流的讀寫位置處于開頭(或結(jié)尾)處。
4、 文件或流已用只讀、只寫或可讀可寫方式打開。
5、 僅用于輸入的變量的值,沒有被子程序所修改。
6、 指針非空。
7、 傳入子程序的數(shù)組或其他容器至少能容納X個數(shù)據(jù)元素。
8、 表已經(jīng)初始化,存儲著真實的數(shù)據(jù)。
9、 子程序開始(或結(jié)束)執(zhí)行時,某個容器是空的(滿的)。
注:斷言主要是用于開發(fā)和維護(hù)階段,而在生成產(chǎn)品代碼時并不編譯到目標(biāo)代碼中,以免降低系統(tǒng)性能。
1、建立自己的斷言機(jī)制
※ C++、Java、VB在內(nèi)的很多語言都支持?jǐn)嘌浴?br/>※ C++中標(biāo)準(zhǔn)的assert宏并不支持文本信息。
2、使用斷言的指導(dǎo)建議
斷言的指導(dǎo)建議:
用錯誤處理代碼來處理預(yù)期會發(fā)生的狀況,用斷言來處理決不應(yīng)該發(fā)生的狀況。
異常發(fā)生,觸發(fā)斷言的情況下,就應(yīng)該修改程序的源代碼并重新編譯。避免把需要執(zhí)行的代碼放到斷言中
※ 一種危險的斷言使用方法(如果斷言關(guān)閉,代碼不能被編譯):
Debug.Assert( PerformAction() )
※ 安全地使用斷言
actionPerformed = PerformAction()
Debug.Assert( actionPerformed )用斷言來注解并驗證前條件和后條件
※ 如果數(shù)據(jù)來源于系統(tǒng)外部,那么就應(yīng)該用錯誤處理代碼來檢查和處理非法的數(shù)值。
※ 如果變量的值來源于可信的系統(tǒng)內(nèi)部,那么使用斷言是很合適的。對于高健壯性的代碼,應(yīng)該先使用斷言再處理錯誤
※ Microsoft Word,在其代碼中,對應(yīng)該始終為真的條件都加上了斷言,但同時也用錯誤處理代碼處理了這些錯誤,以應(yīng)對斷言失敗的情況。
三、錯誤處理技術(shù)
如何處理那些預(yù)料中的程序錯誤:
※ 返回中立值、換用下一個正確數(shù)據(jù)、返回與前次相同的值、換用最接近的有效值、在日志文件中記錄警告信息、返回一個錯誤碼、調(diào)用錯誤處理子程序或?qū)ο蟆@示出錯信息或關(guān)閉程序。
1、返回中立值
※ 有時,處理錯誤的最佳做法就是繼續(xù)執(zhí)行操作并簡單地返回一個沒有危害的數(shù)值。
※ 例如:數(shù)值操作可以返回0、字符串操作可以返回空字符串、指針操作可以返回空指針。
2、換用下一個正確的數(shù)據(jù)
※ 例如:數(shù)據(jù)庫記錄讀取、文件行信息讀取等。
3、返回與前次相同的數(shù)據(jù)
4、換用最接近的合法值
※ 例如:汽車的速度表無法顯示負(fù)的速度,所以在倒車時,它簡單的顯示為0。
5、把警告信息記錄到日志文件中
※ 要考慮是否能夠安全地公開它,或者是否需要對其進(jìn)行加密或?qū)嵤┢渌绞降谋Wo(hù)。
6、返回一個錯誤碼
※ 可以決定在讓系統(tǒng)得某些部分處理錯誤,其他部分則不在本地(局部)處理錯誤,而只是簡單地報告說有錯誤發(fā)生。
采用方法:
※ 設(shè)置一個狀態(tài)變量的值
※ 用狀態(tài)值作為函數(shù)的返回值
※ 用語言內(nèi)建的異常機(jī)制拋出一個異常
注:如果安全性很重要,請確認(rèn)調(diào)用方的子程序總會檢查返回的錯誤嗎。
7、調(diào)用錯誤處理子程序或?qū)ο?br/>※ 可以把錯誤處理都集中在一個全局的錯誤處理子程序或?qū)ο笾羞M(jìn)行。
※ 優(yōu)點:能把錯誤處理的職責(zé)集中在一起,從而讓調(diào)試更為簡單。
缺點:整個程序都要知道這個集中點,并與之緊密耦合。
8、當(dāng)錯誤發(fā)生時顯示出錯消息
※ 可以把錯誤處理的開銷降低。
9、用最妥當(dāng)?shù)姆绞皆诰植刻幚礤e誤
※ 給程序員帶來靈活度的同時,也帶來了顯著的風(fēng)險。即:系統(tǒng)的整體性能將無法滿足對其正確性或可靠性的需求。
10、關(guān)閉程序
※ 適用于人身安全攸關(guān)的應(yīng)用程序。
健壯性與正確性
※ 處理錯誤最恰當(dāng)?shù)姆绞揭鶕?jù)出現(xiàn)錯誤的軟件的類別而定。錯誤處理有時更側(cè)重于正確性,有時更側(cè)重于健壯性。
※ 正確性:意味著永不返回不準(zhǔn)確的結(jié)果,哪怕不返回結(jié)果也比返回不準(zhǔn)確的結(jié)果要好。
※ 健壯性:意味著要不斷嘗試采取某些措施,以保證軟件可以持續(xù)地運轉(zhuǎn)下去,哪怕有時做出一些不夠準(zhǔn)確的結(jié)果。
高層次設(shè)計對錯誤處理方式的影響
※ 在整個程序里采用一致統(tǒng)一的方式來處理非法的參數(shù)。
※ 確定一種通用的處理錯誤參數(shù)的方法,是架構(gòu)層次(高層次)的設(shè)計決策。
※ 一旦確定了某種方法,就要確保始終如一地貫徹這一方法。
※ 請在每個系統(tǒng)調(diào)用后檢查錯誤碼。
四、異常
處理異常建議:
1、用異常通知程序的其他部分,發(fā)生了不可忽略的錯誤
※ 異常機(jī)制的優(yōu)越之處就在于它能提供一種無法被忽略的錯誤通知機(jī)制。
2、只在真正例外的情況下才拋出異常
※ 僅在其他編碼實踐方法無法解決的情況下才使用異常。
※ 異常同斷言相似:都是用來處理那些不僅罕見甚至永遠(yuǎn)不該發(fā)生的情況。
※ 異常的取舍:
1、異常是一種強(qiáng)大的用來處理預(yù)料之外的情況途徑。
2、程序的復(fù)雜度因此增加、性能也可能降低。
3、不能用異常來推卸責(zé)任
※ 能在局部處理的錯誤,就應(yīng)該在局部處理,不能當(dāng)成異常拋出。
4、避免在構(gòu)造函數(shù)和析構(gòu)函數(shù)中拋出異常,除非你在同一地方把他們捕獲
※ 如果在構(gòu)造函數(shù)中拋出異常,就不會調(diào)用析構(gòu)函數(shù),從而造成潛在的資源泄漏。
5、在恰當(dāng)?shù)某橄髮哟螔伋霎惓?br/>※ 當(dāng)你決定把一個異常傳給調(diào)用方時,請確保異常的抽象層次與子程序接口的抽象層次相一致。
6、在異常消息中加入關(guān)于導(dǎo)致異常發(fā)生的全部信息
※ 要確保異常信息中含有為理解異常拋出原因所需要的全部信息。
7、避免使用空的catch語句
※ 注釋或日志記錄信息對這一情況文檔化。
8、了解所有函數(shù)庫可能跑出的異常
※ 一定要了解所用的函數(shù)庫都會拋出哪些異常。
※ 未能捕獲由函數(shù)庫拋出的異常將會導(dǎo)致程序崩潰。
9、考慮創(chuàng)建一個集中的異常報告機(jī)制
※ 能為一些與異常有關(guān)的信息提供一個集中的存儲,如發(fā)生的異常種類、每個異常該如何被處理以及如何格式化異常信息。
10、把項目中對異常的使用標(biāo)準(zhǔn)化
※ 如果使用象C++一樣的語言,就應(yīng)該規(guī)定到底可以拋出哪些種類的異常。(考慮只拋出std::exception基類派生出的對象)
※ 考慮創(chuàng)建項目特定的異常類(用作項目可能異常的基類,這樣就能把紀(jì)錄日志、報告錯誤等操作集中起來并標(biāo)準(zhǔn)化)。
※ 規(guī)定在何種場合允許代碼使用try-catch語句在局部對錯誤進(jìn)行處理。
※ 規(guī)定在何種場合允許代碼拋出不在局部進(jìn)行處理的異常。
※ 確定是否要使用集中的異常報告機(jī)制。
※ 規(guī)定是否允許在構(gòu)造函數(shù)和析構(gòu)函數(shù)中使用異常。
11、考慮異常的替換方案
※ 請考慮你的系統(tǒng)是否真的需要異常。
五、隔離程序,使之包容由錯誤造成的損害
隔欄(barricade)是一種容損策略。
1、在類的層次采用這樣的方法:
※ 類的public方法可以假定數(shù)據(jù)是不安全的,它們要負(fù)責(zé)檢查數(shù)據(jù)并進(jìn)行清理,一旦類的公用方法接受了數(shù)據(jù),那么類的私有方法就可以假定數(shù)據(jù)都是安全的了。
2、隔欄與斷言的關(guān)系:
※ 隔欄的使用使斷言和錯誤處理有了清晰的區(qū)別。
※ 隔欄外部的程序應(yīng)該適用錯誤處理技術(shù),而隔欄內(nèi)部的程序就應(yīng)該使用斷言技術(shù)。
六、輔助調(diào)試的代碼
防御式編程的另一個重要方面就是,適用調(diào)試助手(輔助調(diào)試代碼)。
1、不要自動地把產(chǎn)品版的限制強(qiáng)加于開發(fā)版之上
※ 程序員常常有這樣一個誤區(qū):即認(rèn)為產(chǎn)品級軟件的種種限制也應(yīng)該在開發(fā)版本中得到體現(xiàn)(速度、對資源的限制)。
※ 應(yīng)該在開發(fā)階段犧牲一些速度和資源使用,來換取一些可以讓開發(fā)順暢的內(nèi)置工具(輔助代碼)。
2、盡早引入輔助調(diào)試代碼
※ 越早引入輔助調(diào)試的代碼,它能提供的幫助也就越大。
3、采用進(jìn)攻式編程
※ 應(yīng)該以這么一種方式來處理異常:在開發(fā)階段讓它顯現(xiàn)出來,而在產(chǎn)品代碼運行時讓它能夠自我恢復(fù)。--- “進(jìn)攻式編程”
進(jìn)行進(jìn)攻式編程的方法:
確保斷言語句是程序終止運行。
完全填充分配到的所有內(nèi)存,這樣可以讓你檢測到內(nèi)存分配錯誤。
4、計劃移除調(diào)試輔助的代碼
※ 要事先做好計劃,避免調(diào)試代碼和程序代碼糾纏不清。
采取的方法:
※ 使用類似ant和make這樣的版本控制工具和make工具
(可以從同一套源碼編譯出不同版本的程序)
※ 使用內(nèi)置的預(yù)處理器
※ 編寫你自己的預(yù)處理器
※ 使用調(diào)試存根(stubs)
(兩套方案—開發(fā)版本和發(fā)布版本代碼)
七、確定在產(chǎn)品代碼中該保留多少防御式代碼
八、對防御式編程采取防御的姿態(tài)
看完這篇文章,你們學(xué)會使用防御式編程了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。