用好 Require,check,assert,寫好 Kotlin 代碼

在編碼的時候,我們需要做很多的檢測判斷,比如某個變量是否爲null,某個成員屬性是否爲true,執行某個操作結果是否成功。比如像下面的這段代碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var isDiskMounted = true

fun createNewFile(file: File?): Boolean {
    return if (isDiskMounted) {
        if (file != null) {
            file.createNewFile()
            if (file.exists()) {
                true
            } else {
                println("Create file($file) failed")
                false
            }
        } else {
            println("File($file) is null")
            false
        }
    } else {
        println("Disk is not mounted")
        false
    }
}

上面的代碼實現了

  • 檢測磁盤是否掛載
  • 檢測file參數是否爲null
  • 檢測執行操作結果是否成功(file.exists())

但是上面的代碼也有一些問題

  • 太多的if else 檢測,層級產生,不夠平
  • 多個方法出口
  • 更不容易發現異常和錯誤(有點類似fail safe模式)

使用今天的知識點改造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun createNewFileV2(file: File?): Boolean {
    check(isDiskMounted) {
        "Disk is not mounted"
    }

    requireNotNull(file) {
        "file is null"
    }

    file.createNewFile()

    assert(file.exists()) {
        "createNewFileV2 file($file) does not exist"
    }
    return true
}
  • 方法體沒有多餘層級,比較平
  • 單個方法出口
  • 更快更早發現問題(有點類似fail fast)
  • file.createNewFile()執行時可以不需要再使用file?.createNewFile() 這一點是因爲使用了Contract

require

  • require(boolean) 用來檢測方法的參數,當參數boolean爲false時,拋出IllegalArgumentException

示例代碼

1
2
3
4
5
6
7
8
9
10
11
12
fun readFileContent(file: File?): String {
    //判斷file不能爲null
    requireNotNull(file)

    //判斷文件必須可讀,並提供錯誤的信息
    require(file.canRead()) {
        "readFileContent file($file) is not readable"
    }

    //read file content
    return "Your file content"
}

變種方法

  • fun require(value: Boolean)
  • fun require(value: Boolean, lazyMessage: () -> Any)
  • fun <T : Any> requireNotNull(value: T?)
  • fun <T : Any> requireNotNull(value: T?, lazyMessage: () -> Any)

check

  • check(boolean)用來檢測對象的狀態(屬性),如果boolean爲false,拋出異常IllegalStateException

示例代碼

1
2
3
4
5
6
7
8
9
10
11
12
13
class Engine {
    var isStarted = false

    fun speedUp() {
        check(isStarted) {
            "Engine is not started, cannot be speed up now"
        }

        //speed up the engine
    }


}

變種方法

  • fun check(value: Boolean, lazyMessage: () -> Any)
  • fun <T : Any> checkNotNull(value: T?)
  • fun <T : Any> checkNotNull(value: T?, lazyMessage: () -> Any)

assert

  • assert(boolean) 用來檢測執行結果,當boolean爲false時,拋出AssertionError。但是需要在開啓對應的JVM選項時才生效。

示例代碼

1
2
3
4
5
6
7
8
fun makeFile(path: String) {
    val file = File(path)
    file.createNewFile()

    assert(file.exists()) {
        "make File($file) failed"
    }
}

使用順序

  • 先使用check檢測對象的狀態
  • 再使用require檢測方法的參數合法性
  • 執行操作後,使用assert校驗結果

關於lazyMessage

崩潰更多了,怎麼辦

  • 是的,上面無論是require,check,assert都會在發現錯誤的時候拋出異常
  • 這是爲了讓問題更早發現,這就是它們的哲學
  • 如果想要考慮穩定的話,可以在業務側 debug下崩潰,非debug下捕獲吞掉異常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fun main() {
    createNewFile(null)
    safeRun {
        createNewFileV2(null)
    }
}

private val isDebug = true

fun safeRun(block: () -> Unit) {
    try {
        block()
    } catch (t: Throwable) {
        t.printStackTrace()
        if (isDebug) {
            throw  t
        }
    }
}

更多文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章