臆想實現:合法實現但不滿足介面規範未經檢查的層面。例:Stack 介面除型別簽名外,還隱含不變量:
push(o) 後緊接 pop() 應回傳 o。isEmpty()=true 時 pop() 應拋 RuntimeException。Java 介面無法直接表達此類不變量,但可透過斷言與單元測試自動檢查。
在代碼關鍵點置入布爾檢查。三種形式:
介面規範最適用前兩者。iContract 等預處理器將 Javadoc 註釋斷言轉為 Java 檢查代碼,可選保留於產品版(除效能關鍵路徑,開銷可接受,保留利於用戶端錯誤診斷)。例:
public interface Stack {
/** @pre ! this.isEmpty() */
public Object pop();
public void push(Object top);
public boolean isEmpty();
}
斷言有助確保附加不變量生效、作為文檔,但表達力受限於布爾表達式(無法表達「push 後 pop 回傳同一物件」),宜與單元測試協力。
為介面提供單元測試套件(如 JUnit),可自動檢查所有實現是否滿足。單元測試:
equals 還是 ==?),測試碼一目了然。
public void testPushAndPop() {
Stack s = new MyStack();
Object o = new Object();
s.push(o);
assertTrue(o == s.pop()); // 明確要求同一物件
}
public void testPopOnEmpty() {
Stack s = new MyStack();
assertTrue(s.isEmpty());
try { s.pop(); }
catch (RuntimeException e) { return; }
throw new RuntimeException("pop on empty stack does not fail");
}
| 層級 | 語言 | 表達力 | 可檢查性 |
|---|---|---|---|
| 1 | 型別系統 | 低 | 完全靜態、自動、全覆蓋 |
| 2 | 斷言 | 中 | 運行時檢查、部分覆蓋 |
| 3 | 單元測試 | 高 | 運行時、樣本覆蓋、非窮盡 |
表達力與可檢查性存在根本張力。結合三者可兩全其美:型別系統守基礎,斷言守關鍵契約,單元測試守複雜行為。
斷言與單元測試提供可檢查介面規範,高效避免臆想實現,檢查不變量互補。理想介面兩者兼備。這不僅捕捉錯誤,更助實現者編碼時即正確遵循契約,提升生產力與愉悅感。