百萬分之一
作為一個勤奮的開發人員,您已經為幾個需要更好地訪問複雜的大量資料存放區的客戶安裝了一個應用程式,它編寫良好,而且經過了充分測試。
對每個客戶,現場測試階段都暢通無阻地通過了。您在去銀行的路上,心裡極少考慮這六個月來的軟體審查,這時您的傳呼機響了起來。您的一個客戶在使用您的軟體運行一個報表時,系統崩潰了。
您趕到出事地點,運行了一個隨機測試。工作良好。您運行另一個。沒出現問題。您又運行了數百個測試。還是沒有問題。您又檢查了持續六個月運行這個應用程式的其它客戶。沒有投訴。
您重複運行那個引起問題的報表。崩潰!怎麼回事?
破壞者資料錯誤模式
許多程式需要頻繁訪問和處理內部儲存的資料來執行各種複雜的任務。這種資料可以從記憶體中的大型結構、資料庫或網路上檢索得到。
這類程式非常容易遭受損壞的內部資料引起的崩潰。我稱這種錯誤模式為破壞者資料模式,是因為這種資料可以無限期地存在於系統中(很象冷戰中的潛伏間諜一樣),不引發任何問題,直到訪問一段特定的資料時,損壞的資料才象炸彈一樣爆炸。
文法原因
假定我們有一個 JDBC 應用程式,它儲存了一個名為 Mapping 的資料庫表,該表將 String 的名稱映射到一系列元素的集合。(請參閱 參考資料,以擷取關於 JDBC API 的更多資訊。)每個集合中的每個元素都引用另一個表中的一個關鍵字(該表名為 Properties,包含這些元素的不同已知屬性)。
這樣說吧,Mapping 和 Properties 表最初都是從一個文字檔中讀取的,這個文字檔由外部源( 外部意為不是內部產生的任意資料來源)發展而來,而在外部源中,每行都以一個名稱開頭,後面跟著對應集合的表達,如下所示:
清單 1. 樣本,外部源文字檔
In the Mapping file:
apples {macintosh, gala, golden-delicious}
trees {elm, beech, maple, pine, birch}
rocks {quartz, limestone, marble, diamond}
...
In the Properties file:
macintosh {color: red, taste: sour}
gala {color: red, taste: sweet}
diamond {color: clear, rigidity: hard, value: high}
...
可以對 Mapping 和 Properties 表條目進行文法分析並將其傳遞到一個方法中,此方法會把這些條目插入到一個資料庫中。但這種方法存在潛在的缺陷。例如,假定我們已經編寫了一個處理 JDBC 相容資料庫的類。遵照 JDBC API,我們可以定義一個 PreparedStatement 對象並使用它把資訊傳遞到資料庫中,如下所示:
清單 2. 使用 StreamTokenizer 插入域和地區字串
...
PreparedStatement insertionStmt =
con.prepareStatement("INSERT INTO MAPPING VALUES(?,?)");
...
public void insertEntry(String domain, String range)
throws SQLException {
insertionStatement.setString(1, domain);
insertionStatement.setString(2, range);
insertionStatement.executeUpdate();
}
以這種方式插入兩個 String 合適與否取決於從文字檔中擷取 String 的方式。例如,假定一個簡單的Regex匹配工具被用來將每一行拆分成兩個 String :
一個 String 包含第一個 String 之前的全部字元。
一個 String 包含第一個 String 之後的全部字元。