防禦式程式設計是提高軟體質量技術的有益輔助手段。防禦式程式設計的主要思想是:子程式應該不因傳入錯誤資料而被破壞,哪怕是由其他子程式產生的錯誤資料。這種思想是將可能出現的錯誤造成的影響控制在有限的範圍內。
1. 在非法輸入(invalid inputs)中保護你的程式
乙個好程式,在非法輸入的情況下,要麼什麼都不輸出,要麼輸出錯誤資訊。有幾種方法來防止非法的輸入:
(1)檢查來自於外部資源(external sources)的所有資料的值,例如**於網路的資料的值,**於檔案的資料的值。檢查的目的是保證資料值在乙個允許的範圍內。
(2)檢查每乙個例程(routine)的輸入引數值。
一旦非法輸入被發現,那麼應該根據情況進行處理。防禦式程式設計的最佳的形式是在一開始就不引入錯誤。
2. 斷言(assertions)
乙個斷言通常是乙個例程(routines)或者乙個巨集(marcos)。每個斷言通常含有兩個引數:乙個布林表示式(a boolean expression)和乙個訊息(a message)。乙個布林表示式的反面表示了乙個錯誤。c 標準庫提供了乙個 assert 巨集,它只帶有乙個引數,用法如下:
assert(1 == 0); // 注意 boolean expression 不要加引號
使用 assert 巨集,需要包含標頭檔案 cassert 或者
assert.h,執行上面語句的結果是程式終止執行,輸出與下面訊息類似的訊息:
assertion failed: 1 == 0, file d:\我的文件\visual studio projects\learning\assert\assert.cpp, line 9
通常來說,我們會定義自己的 assert 巨集,其目的有兩個:
(1)新增引數,例如新增乙個訊息引數,使得 assert 巨集輸出更為豐富的資訊。
(2)改變 assert 的行為內容。c 標準庫中的 assert 巨集將中斷程式,實際上,我們可以讓程式繼續執行而不中斷或者進入除錯狀態等,另外還可以控制訊息輸出的目標,即控制訊息是輸出到控制台還是文字檔案,甚至是通過網路發出。
下面是乙個 c++ 實現的斷言:
#ifdef _debug
#define assert(exp, message) \
\ }
#else
#define assert(exp, message)
#endif
執行 assert(1 == 0, "error"); 結果為:
assertion failed: 1 == 0
message: error
line: 24
file: d:\我的文件\visual studio projects\learning\assert\assert.cpp
使用斷言應該注意一下的幾個問題:
1)對非預期錯誤使用斷言
斷言中的布林表示式的反面一定要描述乙個非預期錯誤,下面所述的在一定情況下為非預期錯誤的一些例子:
(1)空指標。
(2)輸入或者輸出引數的值不在預期範圍內。
(3)陣列的越界。
非預期錯誤對應的就是預期錯誤,我們通常使用錯誤處理**來處理預期錯誤,而使用斷言處理非預期錯誤。在**執行過程中,有些錯誤永遠不應該發生,這樣的錯誤是非預期錯誤。斷言可以被看成是一種可執行的注釋,你不能依賴它來讓**正常工作(《code complete 2》)。例如:
int nres = f(); // nres 由 f 函式控制, f 函式保證返回值一定在 -100 ~ 100
assert(-100 <= nres && nres <= 100); // 斷言,乙個可執行的注釋
由於 f 函式保證了返回值處於 -100 ~ 100,那麼如果出現了 nres 不在這個範圍的值時,就表明乙個非預期錯誤的出現。後面會講到「隔欄」,那時會對斷言有更加深刻的理解。
2)不要把需要執行的**放入斷言中
斷言用於軟體的開發和維護,而通常不在發行版本中包含斷言。
需要執行的**放入斷言中是不正確的,因為在發行版本中,這些**通常不會被執行,例如:
assert(f()); // f 函式通常在發行版本中不會被執行
而使用如下方法則比較安全:
res = f();
assert(res); // 安全
3)對**於內部系統的可靠的資料使用斷言,而不要對外部不可靠的資料使用斷言,對於外部不可靠資料,應該使用錯誤處理**。再次強調,把斷言看成可執行的注釋。
前條件(preconditions)和後條件(postconditions)
前條件是呼叫方**在呼叫例程(routines)或者例項化物件之前要確保為真的條件,後條件是例程執行後或者類例項化後應滿足的條件。下面是乙個例子:
// 前條件,這裡 nnum1 和 nnum2 的取值被前面**所約束並保證取值在 -50 ~ 50
assert(-50 <= nnum1 && nnum1 <= 50, "add_nnum1");
assert(-50 <= nnum2 && nnum2 <= 50, "add_nnum2");
int nres = add(nnum1, nnum2);
// 後條件
assert(-100 <= nres && nres <= 100, "add_nres");
注意,由於 nnum1 和 nnum2 取值範圍已經被約束,因此可以使用斷言,但是如果 nnum1 和 nnum2 的值**於不可靠的外部系統,那麼應該使用錯誤處理**,而不是使用斷言。
3. 錯誤處理技術
這裡主要講述如何處理預期錯誤。
(1)終止程式執行
有些錯誤非常嚴重,如果出現,那麼最好就的做法就是讓程式終止並且讓使用者重啟程式。例如,對於顯示 x 光片的繪圖程式,如果資料出錯,那麼就關閉程式,這個時候關閉程式要遠遠好於顯示錯誤的資料。
(2)繼續程式執行
有時候,錯誤出現了,但是沒有必要去關閉程式,那麼就有兩種處理方案:
a. 在例程中處理錯誤
例如讓例程返回乙個中立值,這是一種可行的方法,中立值在有些語言裡面被描述為「型別的預設值」,例如整形的中立值為 0,指標的中立值為 null(或 null 等)
b. 在例程外處理錯誤
返回乙個錯誤碼也是可行的,返回錯誤碼意味著,錯誤將交由其他程式部分來處理,而不是本例程處理。
對於出現了錯誤,而沒有終止程式的執行,這時候,你可以在日誌檔案中新增乙個警告資訊。
抉擇:正確性和健壯性
有些程式要求非常高的正確性,而有些程式要求較高的健壯性,通常兩者我們只能取其一。
(1)正確性意味著結果永遠是正確的,如果出錯,寧願不給出結果也不要給定乙個不準確的值。
(2)健壯性意味著通過一些措施,保證軟體能夠正常執行下去,即使有時候會有一些不準確的值出現。
4. 隔欄(barricades)
防禦式程式設計(二)
防禦式程式設計是提高軟體質量技術的有益輔助手段。防禦式程式設計的主要思想是 子程式應該不因傳入錯誤資料而被破壞,哪怕是由其他子程式產生的錯誤資料。這種思想是將可能出現的錯誤造成的影響控制在有限的範圍內。1.在非法輸入 invalid inputs 中保護你的程式 乙個好程式,在非法輸入的情況下,要麼...
防禦式程式設計
防禦式程式設計 防禦式程式設計的主要思想是 子程式應該不因傳入錯誤資料而被破壞,哪怕是由其他子程式產生的錯誤資料。更一般地說,其核心思想是要承認程式都會有問題,都需要修改,聰明的程式設計師應該根據這一點來程式設計序。我們心裡應該自始至終考慮各種各樣的錯誤處理機制 在區域性處理錯誤 使用錯誤碼來傳遞錯...
防禦式程式設計
場景 交易終端支援市中核算,必須需要處理當日的委託和成交資料,現系統的委託和成交在本地快取和遠端快取中各有乙份。思路 核算優先使用本地快取,本地快取不存在那麼就 防禦式 取遠端快取。實現package demo.design.defensive 專案 demo design defensive 場景...