Key Takeaways(劃重點):
- require對外、check對內(nèi),組成了協(xié)議的前置條件
- assert是協(xié)議的后置條件
接觸過Design by contract或OCL(Object Constraint Language)或平時設(shè)計比較嚴謹?shù)耐瑢W應該知道,一個良好的接口設(shè)計/文檔其實是應該包括了接口的前置條件(即滿足什么條件才可以調(diào)用這個接口)和后置條件的(執(zhí)行完畢這個接口后,哪些是真)。在2000年初期MDA (Model-driven Architecture)還比較紅火的時候续室,很多模型師、架構(gòu)師都會在接口中追加此類定義谒养。這個追加行為挺狰,除了本身有利于源代碼的輸出(是的,當時MDA的口號其實就是以后不需要碼農(nóng)买窟,只要模型師的)丰泊,確實可以讓接口的定義更完整、清晰始绍,顯得專業(yè)味十足瞳购。
現(xiàn)在MDA雖然不怎么再提到,但其科普的前置/后置條件還是一定程度幫助了軟件業(yè)的完善亏推。Kotlin作為一個比較現(xiàn)代的語言苛败,在汲取了多類語言和設(shè)計概念后,很多原先其他語言需要特定實現(xiàn)(或重復發(fā)明輪子)的事情径簿,在Kotlin的標準庫就自帶了,譬如require / check / assert 對于前置/后置條件的支持嘀韧。
先看下三者的定義:
-
require(Boolean) throw
IllegalArgumentException
-
check(Boolean) throw
IllegalStateException
-
assert(Boolean) throw
AssertionError
其實對應著看到各自的輸出篇亭,應該能猜測到一些東西。譬如
-
IllegalArgumentException
: 傳入的參數(shù)有問題 -
IllegalStateException
:自身狀態(tài)不對 -
AssertionError
:和預估的不一樣 (在后置條件的維基百科中其實就是這么定義的)Postconditions are sometimes tested using assertions within the code itself
所以總結(jié)下來锄贷,大概就是這么回事了:
- require負責檢查輸入的參數(shù)译蒂,如果有問題曼月,拋出
IllegalArgumentException
- check負責檢查自身是否萬事俱備可以執(zhí)行了,如果不是柔昼,拋出
IllegalStateException
- require + check就是在做前置條件的檢查哑芹,通過了才可以執(zhí)行真正的程序邏輯
- assert負責確保程序執(zhí)行完畢后的結(jié)果/內(nèi)部狀態(tài)是否符合預期,如果不是捕透,拋出
AssertionError
一個完整應用了這幾個檢查的代碼大概如下(一個方法用于單次執(zhí)行指定的sql語句聪姿,每次執(zhí)行連接數(shù)據(jù)庫并在執(zhí)行完畢后釋放連接(老土的demo,沒有連接池-_-)):
fun execute(sql: String) : Unit {
// 輸入?yún)?shù)的檢查
require(!sql.isNullOrBlank()) {
"被執(zhí)行的sql語句不能為空"
}
// 自身狀態(tài)檢查
check(!this.host.isNullOrBlank()) {
"sql server未指定"
}
/*
* conn = ...
* conn.execute(sql)
* conn.disconnect()
*/
// 執(zhí)行完畢后狀態(tài)檢查
assert(!conn.isConnected) {
"每次執(zhí)行完畢后都需要釋放連接"
}
}
上面的require和check的順序乙嘀,沒有一定的誰先誰后末购,這個純粹看個人風格/習慣。不過如果涉及到某些執(zhí)行/檢查比較費資源時虎谢,還是讓不費資源的優(yōu)先執(zhí)行為上盟榴。
Kotlin標準庫的這幾個函數(shù),雖小卻清晰的用代碼來定義了契約婴噩,講究協(xié)作的今天擎场,還是挺需要的。
希望這篇博文能對你有所幫助几莽,喜歡的話點個贊吧迅办!
更多Kotlin的實用技巧,請參考《Kotlin邊用邊學系列》