Key Takeaways(劃重點(diǎn)):
- require對(duì)外、check對(duì)內(nèi),組成了協(xié)議的前置條件
- assert是協(xié)議的后置條件
接觸過(guò)Design by contract或OCL(Object Constraint Language)或平時(shí)設(shè)計(jì)比較嚴(yán)謹(jǐn)?shù)耐瑢W(xué)應(yīng)該知道,一個(gè)良好的接口設(shè)計(jì)/文檔其實(shí)是應(yīng)該包括了接口的前置條件(即滿足什么條件才可以調(diào)用這個(gè)接口)和后置條件的(執(zhí)行完畢這個(gè)接口后,哪些是真)。在2000年初期MDA (Model-driven Architecture)還比較紅火的時(shí)候,很多模型師、架構(gòu)師都會(huì)在接口中追加此類定義。這個(gè)追加行為,除了本身有利于源代碼的輸出(是的,當(dāng)時(shí)MDA的口號(hào)其實(shí)就是以后不需要碼農(nóng),只要模型師的),確實(shí)可以讓接口的定義更完整、清晰,顯得專業(yè)味十足。

現(xiàn)在MDA雖然不怎么再提到,但其科普的前置/后置條件還是一定程度幫助了軟件業(yè)的完善。Kotlin作為一個(gè)比較現(xiàn)代的語(yǔ)言,在汲取了多類語(yǔ)言和設(shè)計(jì)概念后,很多原先其他語(yǔ)言需要特定實(shí)現(xiàn)(或重復(fù)發(fā)明輪子)的事情,在Kotlin的標(biāo)準(zhǔn)庫(kù)就自帶了,譬如require / check / assert 對(duì)于前置/后置條件的支持。
先看下三者的定義:
-
require(Boolean) throw
IllegalArgumentException -
check(Boolean) throw
IllegalStateException -
assert(Boolean) throw
AssertionError
其實(shí)對(duì)應(yīng)著看到各自的輸出,應(yīng)該能猜測(cè)到一些東西。譬如
-
IllegalArgumentException: 傳入的參數(shù)有問(wèn)題 -
IllegalStateException:自身狀態(tài)不對(duì) -
AssertionError:和預(yù)估的不一樣 (在后置條件的維基百科中其實(shí)就是這么定義的)Postconditions are sometimes tested using assertions within the code itself
所以總結(jié)下來(lái),大概就是這么回事了:
- require負(fù)責(zé)檢查輸入的參數(shù),如果有問(wèn)題,拋出
IllegalArgumentException - check負(fù)責(zé)檢查自身是否萬(wàn)事俱備可以執(zhí)行了,如果不是,拋出
IllegalStateException - require + check就是在做前置條件的檢查,通過(guò)了才可以執(zhí)行真正的程序邏輯
- assert負(fù)責(zé)確保程序執(zhí)行完畢后的結(jié)果/內(nèi)部狀態(tài)是否符合預(yù)期,如果不是,拋出
AssertionError
一個(gè)完整應(yīng)用了這幾個(gè)檢查的代碼大概如下(一個(gè)方法用于單次執(zhí)行指定的sql語(yǔ)句,每次執(zhí)行連接數(shù)據(jù)庫(kù)并在執(zhí)行完畢后釋放連接(老土的demo,沒(méi)有連接池-_-)):
fun execute(sql: String) : Unit {
// 輸入?yún)?shù)的檢查
require(!sql.isNullOrBlank()) {
"被執(zhí)行的sql語(yǔ)句不能為空"
}
// 自身狀態(tài)檢查
check(!this.host.isNullOrBlank()) {
"sql server未指定"
}
/*
* conn = ...
* conn.execute(sql)
* conn.disconnect()
*/
// 執(zhí)行完畢后狀態(tài)檢查
assert(!conn.isConnected) {
"每次執(zhí)行完畢后都需要釋放連接"
}
}
上面的require和check的順序,沒(méi)有一定的誰(shuí)先誰(shuí)后,這個(gè)純粹看個(gè)人風(fēng)格/習(xí)慣。不過(guò)如果涉及到某些執(zhí)行/檢查比較費(fèi)資源時(shí),還是讓不費(fèi)資源的優(yōu)先執(zhí)行為上。
Kotlin標(biāo)準(zhǔn)庫(kù)的這幾個(gè)函數(shù),雖小卻清晰的用代碼來(lái)定義了契約,講究協(xié)作的今天,還是挺需要的。
希望這篇博文能對(duì)你有所幫助,喜歡的話點(diǎn)個(gè)贊吧!
更多Kotlin的實(shí)用技巧,請(qǐng)參考《Kotlin邊用邊學(xué)系列》