软件构造 6-3 Assertions and Defensive Programming
6.3 斷言與防御式編程
一. 斷言 assert
??首先要遵循:以盡量不要引入 bug 的原則(防御式編程)編程。
- 靜態(tài)檢查
- 動態(tài)檢查
- 不可變類型
- 不可變值
- 不可變引用
??若無法避免,縮小 bug 的范圍:
- 限定在一個方法內(nèi)部,不擴散
- 盡快失敗,就容易發(fā)現(xiàn)、越早修復(fù)
??斷言就是一種 fail fast,避免 bug 的擴散。
1. assertion
??斷言:在開發(fā)階段的代碼中嵌入,檢驗某些“假設(shè)”是否成立。若成立,表明程序運行正常,否則表明存在錯誤。
public class AssertionTest {public static void main(String[] args) {int number = -5; // assumed number is not negative// This assert also serve as documentationassert (number >= 0) : "number is negative: " + number;// do somethingSystem.out.println("The number is " + number);} }??每個斷言都包含一個您認為在程序執(zhí)行時為真的布爾表達式。
- 若不為真將出現(xiàn) AssertionError
- 出現(xiàn) AssertionError ,意味著內(nèi)部某些假設(shè)被違反了
- 增強程序員對代碼質(zhì)量的信心:對代碼所做的假設(shè)都保持正確
??assert 具有以下兩種方式之一:
- assert condition
- assert condition : message:message 可以描述錯誤原因、記錄案發(fā)現(xiàn)場
??有時候為了安全性,防止錯誤對后續(xù)程序造成影響,release 版本使用 assert,但一般不使用。
2. assert 適用范圍
?? assert 可以使用在各種不變量中:
- Internal Invariants 內(nèi)部不變量
- Rep Invariants 表示不變量:checkRep()
- Control Flow Invariants 控制流不變量
- Pre conditions of methods 方法的前置條件
- Post conditions of methods 方法的后置條件,如:
- Control-flow 控制流,如:不使用不分流;判斷是否執(zhí)行到不該執(zhí)行的代碼之后
??其實等價于使用語句如:
default: throw new AssertionError ("must be a vowel, but was: " + vowel);??assert 還可以判斷:
- 指針是否為空
- 傳入方法的數(shù)組或其他容器至少可以包含 X 個數(shù)據(jù)元素
- 一個表被初始化為實數(shù)值
- 當(dāng)方法開始執(zhí)行(或完成)時,容器是空的(或滿的)
- 從一個高度優(yōu)化的、復(fù)雜的方法得到的結(jié)果與從一個較慢但清晰編寫的例程得到的結(jié)果相匹配
??實際上可以對程序執(zhí)行時的某些條件進行驗證。
??實際上, assert
- 斷言主要用于開發(fā)階段,避免引入和幫助發(fā)現(xiàn) bug
- 實際運行階段,不再使用斷言,運行階段對你編寫的程序不可以使用 assert 以保證健壯性。但可以使用異常
- 避免降低性能
- 使用斷言的主要目的是為了在開發(fā)階段調(diào)試程序、盡快避免錯誤
??因此,程序之外的事,不受你控制,不要亂斷言,注意:
- 如:文件/網(wǎng)絡(luò)/用戶輸入等不要檢測。這是因為檢測了也沒辦法
- 斷言只是檢查程序的內(nèi)部狀態(tài)是否符合規(guī)約
- 斷言一旦 false ,程序就停止執(zhí)行
- 你的代碼無法保證不出現(xiàn)此類外部錯誤 ,外部錯誤要使用 Exception 機制去處理。而 assert 一般檢測程序內(nèi)部的錯誤(開發(fā)階段)。
??正常情況下,Java 缺省關(guān)閉斷言,要使用 assert 時要記得打開 (-ea)。
??默認情況下, assert 功能是關(guān)閉的,這是因為
- 斷言不應(yīng)該出現(xiàn)在工程的 release 版本。client 想要挽救 bug 而不是中止程序。
- 斷言非常影響運行時的性能
??開啟 assert 需要使用相應(yīng)的命令或者打開相應(yīng)的按鈕
3. 斷言使用的指導(dǎo)
??assert 與 Exception 的區(qū)別
- 斷言的目標是保證正確性,使用在開發(fā)階段,針對程序員。使用斷言處理“絕不應(yīng)該發(fā)生”的情況
- 錯誤/異常處理的目標是保證健壯性,使用在發(fā)布階段,針對用戶端。使用異常來處理你“預(yù)料到可以發(fā)生”的不正常情況。
??對于 pre-/post-condition ,是否使用 assert 有爭議(前者通常用 Exception, 后者通常用 assert)。爭議的理由有以下幾點
- 不管是否 -ea, spec 中的 pre-/post-conditions 都能夠被保證
- 即使 spec 被違反,也不應(yīng)通過 assert 直接 fail ,而是應(yīng)拋出具體的 RunTimeException 。
??我們使用如下原則:
??如果參數(shù)來自于外部(不受自己控制,如 public 方法的參數(shù)),使用異常處理;如果來自于自己所寫的其他代碼,可以使用斷言來幫助發(fā)現(xiàn)錯誤(例如 post condition 就需要;如 private 方法)。
??異常能做到斷言能做的事。斷言和異常處理都可以處理同樣的錯誤。但由于斷言可以容易地開啟關(guān)閉,故斷言的使用仍然廣泛。
??開發(fā)階段用斷言盡可能消除 bugs 在發(fā)行版本里用異常處理機制處理漏掉的錯誤。
二. 防御性編程及工具
1. 阻止程序的非法輸入
??正確性使得程序員只需對正確數(shù)據(jù)有正確反應(yīng),錯誤數(shù)據(jù)可以隨意處理。但是從健壯性角度,仍然需對錯誤數(shù)據(jù)做出良好的反應(yīng),如無反應(yīng)、提示錯誤信息或不允許輸入錯誤數(shù)據(jù)。這時規(guī)約應(yīng)該添加上對錯誤的處理方法。
??對來自外部的數(shù)據(jù)源要仔細檢查,例如:文件、網(wǎng)絡(luò)數(shù)據(jù)、用戶輸入等。
2. 設(shè)置路障
??不安全的方法(輸入可能不合法的方法)通過檢測手段檢測數(shù)據(jù)輸入是否有錯誤。路障可以使不正確的輸入被過濾掉,使得不安全的方法變成安全的方法。
??可以使用 public 和 private 方法判定,前者為不安全的方法,后者可能是安全的方法。
??類的 public 方法接收到的外部數(shù)據(jù)都應(yīng)被認為是 dirty 的,需要處理干凈再傳遞到private 方法——隔離艙方法、操作間技術(shù)。
??“隔離艙”外部的函數(shù)應(yīng)使用異常處理,“隔離艙”內(nèi)的函數(shù)應(yīng)使用斷言。
??這種方法是代理模式(Proxy 設(shè)計模式)。代理類可以做數(shù)據(jù)校驗。
三. SpotBugs 工具
??SpotBugs 是 Java 靜態(tài)代碼分析工具。
總結(jié)
以上是生活随笔為你收集整理的软件构造 6-3 Assertions and Defensive Programming的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Autofac 批量注入
- 下一篇: c语言rr算法,[判断题] 在RR、PF