按合約設計---
DBC(Design By Contract)是一種簡單而強大的技術,它關注的是用文檔記載(並約定)軟體模組的權利與責任,以確保程式正確性.簡單的說,就是用文檔記載這樣的聲明,並進行校正,以確保程式能做它聲明要做的事情.
Eiffel 發展了這種概念並很好的實現了它,那像Java & .NEt這樣的更流行語言能否支援DBC呢?查了一下資料,Java使用iContract這樣的工具做為前置處理器(perprocesseor),處理作為特殊注釋嵌入在原代碼中的合約.而.NET架構具有前置處理器(perprocesseor)並且支援預先處理指令(如:#if,#elif,#define),那.net能否不藉助其他工具直接就能支援DBC呢??
擺弄了一番,發現.NET沒有單獨的preprocessor,預先處理指令只是讓你感覺有那麼一個preprocessor,所以它不能很好的實現DBC中的前條件(precondition),後條件(postcondition)和不變項(invariant).那.NET沒有preprocessor就沒法實現DBC了嗎?答案不是絕對的,因為DBC的主要內容是斷言(assert)的一種觀念,所以我們能使用斷言(assert)對此進行類比:
iContract中實現percondition代碼如下:
/**
* @pre f >= 0.0 //@pre定義percondition,確定參數 f 必須大於等於0
*/
public float sqrt(float f) { ... }
用C#可以這樣類比實現:
public float sqrt(float f)
{
System.Diagnostics.Debug.Assert(f>=0,"parameter f < 0 ");
......
}
postcondition也可以類似實現:
iContract:
/**
* @pre f >= 0.0
* @post Math.abs((return * return) - f) < 0.001
*/
public float sqrt(float f) { ... }
C#:
public float sqrt(float f)
{
System.Diagnostics.Debug.Assert(f>=0,"parameter f < 0 ");
......
System.Diagnostics.Debug.Assert(Math.abs((result * result) - f)<0.001);
return result;
}
Invariants 也可以如此炮製,這裡就不寫了.
上面說了,使用斷言(assert)實現DBC始終是一種類比實現,也就是說,我們不能使用Assert做DBC能做的每一件事.主要原因斷言(assert)不能沿著繼承層次向下遺傳.這就意味著,如果你重新設定了某個合約的基類方法,你必須把你的修改手工複製到他的底層類中.
只用斷言(assert)實現DBC雖然功能不全,但還是很有必要的,它能根據最初的簡單設計,控制住參數的輸入輸出等,大大增加了程式的"安全性"(降低Bug出現的幾率),使你的程式完成一次品質的飛躍.
最後我列舉 [The Pragmatic Programmer] 中的例子(有的地方做了適當修改),它們存在於我們的常識中.加上這些斷言判斷是多麼的有必要,你往往認為絕對不會出錯的地方,它也存在著Bug隱患.我們很容易掉進"它不可能發生"這樣一種心理狀態.
1. 一個月不可能少於28.
2. 在C#裡: a = 2;b = 3; if(a + b !=5) exit();
3. 不會有內角和不等於180度的三角形.
4. 不存在大於60秒的一分鐘.
5. 在C#中: (a+1) <= a;
P.S: 我想我們實現的介面,抽象類別也應該算是一種DBC,它完全符合DBC的定義,並能在編譯期實行對合約實現地檢查.