防禦式程式設計(二)

2022-08-20 02:24:08 字數 3306 閱讀 3315

防禦式程式設計是提高軟體質量技術的有益輔助手段。防禦式程式設計的主要思想是:子程式應該不因傳入錯誤資料而被破壞,哪怕是由其他子程式產生的錯誤資料。這種思想是將可能出現的錯誤造成的影響控制在有限的範圍內。

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 場景...