代码设计之 Guard clause

  • Post author:
  • Post category:tech
  • Post comments:0 Comments

大家有没有看过这样的代码?就是一个方法里面,整个就是一个if语句。然后这个if语句非常长长长长。。。一屏都放不下,你都不知道这个if语句会在哪里结束。

fun runRule()
    if (rulesEnabled) {
        var maxConsecutiveCount = 0
        var currentConsecutiveCount = 0
        var previousReceiver = ""
        expression.children.forEach {
            if (it is KtBinaryExpression) {
                val currentReceiver = getReceiver(it)
                if (currentReceiver == previousReceiver) {
                    currentConsecutiveCount ++
                } else {
                    currentConsecutiveCount = 1
                    previousReceiver = currentReceiver
                }

                maxConsecutiveCount = max(maxConsecutiveCount, currentConsecutiveCount)
            } else {
                currentConsecutiveCount = 0
                previousReceiver = ""
            }
        }

        if (maxConsecutiveCount >= threshold) {
            report(CodeSmell(issue, Entity.from(expression), "This condition is too complex."))
        }
        val binaryExpressions = condition?.collectByType<KtBinaryExpression>()?.toList()

        if (binaryExpressions != null && binaryExpressions.size > 1) {
            val longestBinExpr = binaryExpressions.reduce { acc, binExpr ->
                if (binExpr.text.length > acc.text.length) binExpr else acc
            }
            val conditionString = longestBinExpr.text
            val count = frequency(conditionString, "&&") + frequency(conditionString, "||") + 1
            if (count >= threshold) {
                report(ThresholdedCodeSmell(issue,
                        Entity.from(condition),
                        Metric("SIZE", count, threshold),
                        "This condition is too complex."))
            }
        }
    }
}

这种代码的缺点是,只要if语句一长,你就很难分清后面的代码哪些是在if里面,哪些不在 if 里面的。此外,这种写法也很容易导致下面的代码太多层的嵌套。嵌套太深的缺点,相信大家都有体会,在这里就不多说了。
像这种代码,有一个好用的技巧,就是使用guard clause。简单说就是,如果if里面的条件不满足,就直接return

fun runRule() {
    if (!rulesEnabled) return

    var maxConsecutiveCount = 0
    var currentConsecutiveCount = 0
    var previousReceiver = ""
    expression.children.forEach {
        if (it is KtBinaryExpression) {
            val currentReceiver = getReceiver(it)
            if (currentReceiver == previousReceiver) {
                currentConsecutiveCount ++
            } else {
                currentConsecutiveCount = 1
                previousReceiver = currentReceiver
            }

            maxConsecutiveCount = max(maxConsecutiveCount, currentConsecutiveCount)
        } else {
            currentConsecutiveCount = 0
            previousReceiver = ""
        }
    }

    if (maxConsecutiveCount >= threshold) {
        report(CodeSmell(issue, Entity.from(expression), "This condition is too complex."))
    }
    val binaryExpressions = condition?.collectByType<KtBinaryExpression>()?.toList()

    if (binaryExpressions != null && binaryExpressions.size > 1) {
        val longestBinExpr = binaryExpressions.reduce { acc, binExpr ->
                                                       if (binExpr.text.length > acc.text.length) binExpr else acc
                                                      }
        val conditionString = longestBinExpr.text
        val count = frequency(conditionString, "&&") + frequency(conditionString, "||") + 1
        if (count >= threshold) {
            report(ThresholdedCodeSmell(issue,
                                        Entity.from(condition),
                                        Metric("SIZE", count, threshold),
                                        "This condition is too complex."))
        }
    }
}

这样一来,看代码的人只要看到方法开头,就知道下面的代码全部是满足 rulesEnabled 的这个条件的。

不要觉得这个东西无所谓,要知道,swift 语言里面有一个 guard 关键字,专门用来做这件事情。

func runRules() {
    guard rulesEnabled else { return }

    //run rules
}

如果一门语言把某一种写法集成到它的特性里面,可想而知这种写法的好处是特别大的。具体的话,大家下次看到这样的代码,自己去体会就好。

Leave a Reply