首选可变参数函数而非 `[]T` 参数
发布者:admin 发表于:446天前 阅读数:732 评论:0

编写一个带有切片参数的函数或方法是很常见的。

func ShutdownVMs(ids []string) error

这只是我编的一个例子,但它与我所写的很多代码相同。 这里的问题是他们假设他们会被调用于多个条目。 但是很多时候这些类型的函数只用一个参数调用,为了满足函数参数的要求,它必须打包到一个切片内。

另外,因为 ids 参数是切片,所以你可以将一个空切片或 nil 传递给该函数,编译也没什么错误。 但是这会增加额外的测试负载,因为你应该涵盖这些情况在测试中。

举一个这类 API 的例子,最近我重构了一条逻辑,要求我设置一些额外的字段,如果一组参数中至少有一个非零。 逻辑看起来像这样:

if svc.MaxConnections > 0 || svc.MaxPendingRequests > 0 || svc.MaxRequests > 0 || svc.MaxRetries > 0 {
    // apply the non zero parameters
}

由于 if 语句变得很长,我想将签出的逻辑拉入其自己的函数中。 这就是我提出的:

// anyPostive indicates if any value is greater than zero.
func anyPositive(values ...int) bool {
    for _, v := range values {
        if v > 0 {
            return true
        }
    }
    return false
}

这就能够向读者明确内部块的执行条件:

if anyPositive(svc.MaxConnections, svc.MaxPendingRequests, svc.MaxRequests, svc.MaxRetries) {
        // apply the non zero parameters
}

但是 anyPositive 还存在一个问题,有人可能会这样调用它:

if anyPositive() { ... }

在这种情况下,anyPositive 将返回 false,因为它不会执行迭代而是立即返回 false。对比起如果 anyPositive 在没有传递参数时返回 true, 这还不算世界上最糟糕的事情。

然而,如果我们可以更改 anyPositive 的签名以强制调用者应该传递至少一个参数,那会更好。我们可以通过组合正常和可变参数来做到这一点,如下所示:

// anyPostive indicates if any value is greater than zero.
func anyPositive(first int, rest ...int) bool {
    if first > 0 {
        return true
    }
    for _, v := range rest {
        if v > 0 {
            return true
        }
    }
    return false
}

现在不能使用少于一个参数来调用 anyPositive