在我们日常的编码过程中,异常算是我们的老朋友了,虽然它给我们的程序带来了 Crash,让我们烦恼无比,但是它也有它存在的道理,只要我们能合理的使用它,我们的代码也会变的更加健壮。
class MainPage {
private var mMode = MODE_HOME
// 其它的一堆代码
fun setMode(mode: Int) {
if (!isSupportMode(mode)) {
throw IllegalStateException("不支持的 mode!")
}
mMode = mode
}
fun refreshPage() {
when (mMode) {
MODE_HOME -> {
// do something
}
MODE_USER_INFO -> {
// do something
}
MODE_SETTINGS -> {
// do something
}
else -> {
throw IllegalStateException("不支持的 mode!")
}
}
}
companion object {
const val MODE_HOME = 0
const val MODE_USER_INFO = 1
const val MODE_SETTINGS = 2
fun isSupportMode(mode: Int): Boolean = mode == MODE_HOME
|| mode == MODE_USER_INFO
|| mode == MODE_SETTINGS
}
}
在上面对这个例子中,我们在执行 setMode、refreshPage 函数时,我们对 mode 可能存在的非预期情况进行了检查,如果它的值并不是我们预期的 MODE_HOME、MODE_USER_INFO、MODE_SETTINGS 三个值之一,这个时候我们一般可以有两种处理方式:
如果 mode 我们没有办法对其进行恢复(设置成一个正确值),或者这个值还有多处依赖,我们恢复了也没办法通知到所有的使用范围,这时候,及时抛出异常,及时止损,因为我们可以认为现在这个不可恢复的错误能被捕获到最根源的地方就是 setMode 和 refreshPage 这两个个函数调用时,如果我们不抛出异常,让这个错误就这么过去了,那么我们后面可能就会引发其它更严重的问题,而且非常难查,因为新出现的问题是由其它的问题引起的,我们面对新问题的时候很难发现产生这个问题的根本问题,这时候可能不管你怎么解 BUG 都解不完,只能重新 review 全部的代码。
如果 mModel 可以恢复,那就简单了,及时恢复吧。
这里可能会有些疑问,我们平时写代码都是尽可能的避免 Crash,那你这里为啥还主动抛出异常让程序 Crash 掉呢?
其实这里我们是有先决条件的,当程序进入到不可恢复的状态了,这时候我们要尽快的结束程序,避免引发更多更复杂的问题,及时止损,当然如果程序可以恢复,我们也应当尽可能的采用返回值的形式拦截错误并恢复,从而保证用户体验。另外我们可以想到,类似于这种问题,一般都是由于我们编码不严谨导致的,换句话说也就是代码写的有漏洞,这时候我们及时抛出异常就非常有利于我们自身发现问题,在自测或者是测试人员测试的环节,这类问题也更容易暴露出来,从而使我们最终上线的代码更加健壮。
在我之前维护的一个项目里,就老是出现很多奇奇怪怪难以解决的问题,最后在 review 整体代码的时候,发现很多之前维护的人在面对问题的时候,都是直接 Try Catch 捕获一下就完事了,该抛出异常的地方就啥也不干,只要不崩溃,就万事大吉,线上的代码从而变得十分的脆弱,进而引发数据不一致、页面恢复错误、用户数据丢失等等问题,最终这块代码因为质量太差,内容又多,只能被完全推翻重写,浪费了人力物力,也造成了很多用户的损失。