优雅地重构 if else

要优雅

假设要写一个将国家名转换为简写的函数,会很自然地想到以下的方法:

fun countryAbbreviate(countryName: String): String {
    if(countryName == "China") {
        return "CHN"
    }
    else if(countryName == "America") {
        return "USA"
    }
    else if(countryName == "Japan") {
        return "JPN"
    }
    else {
        return "Other"
    }
}

if...else if来根据全名返回对应的缩写。当数据量小,只需要几次判断的时候完全没问题,但是一旦数据量大起来,就是无止境的if...else if,代码就变得非常恶心了。必须得重构,稍微好一点的方法是用switch类的条件判断,特别是对于Kotlin这种有语法糖的语言来说,代码一下缩短了一半,可读性也大大增加了。

fun countryAbbreviate(countryName: String): String {
    when(countryName) {
        "China"   -> return "CHN"
        "America" -> return "USA"
        "Japan"   -> return "JPN"
        else      -> return "Other"
    }
}

但这还不够好,每次要增加一个国家,还是得多一条语句。只是将代码紧凑化了,并没有实质性的改变。接下来就要介绍本文的主角了——表驱动法

表驱动法

表驱动法即是根据查表的方式获取值,方法改成表驱动法后的样子:

fun countryAbbreviate(countryName: String): String {

    val countryMap = mapOf(
            "China"   to "CHN",
            "America" to "USA",
            "Japan"   to "JPN"
    )

    if(countryName in countryMap)   return countryMap[countryName]!!
    else                            return "Other"

代码中使用了Map这个键值对数据结构来存储数据,然后只需判断国家名是否在数据中,是就直接返回对应键的值。这样无论数据有多少条,逻辑代码都只用这两行if...else,非常优雅。

但表驱动法的意义可不仅仅只是节省一堆代码,更重要的意义在于:将逻辑,数据分离

相较于直接修改逻辑代码,修改数据要简单,安全得多。

如果是一开始的方式,那每添加一个国家,就要多一条else if,相当于多了一条逻辑。使用了表驱动法,就只用在Map里增加一条数据,数据的来源也非常灵活,比如可以像上面一样来自代码,可以来自配置文件,可以来自API,可以来自数据库...

而且当程序使用者是非专业人员时,想修改数据也非常容易。而逻辑写死在代码里,修改只能是开发人员来,然后经过一系列的部署流程,成本非常高。

总结

表驱动法能够简化代码,将逻辑、数据分离提高程序的扩展性,动态性和可读性

当然,所有事情都是有性价比的,如果是像上面例子一样的简单程序,直接用if...elseswitch来的更快些。