6.2.2. 首选可变参数函数而非 []T 参数

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

  1. func ShutdownVMs(ids []string) error

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

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

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

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

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

  1. // anyPostive indicates if any value is greater than zero.
  2. func anyPositive(values ...int) bool {
  3. for _, v := range values {
  4. if v > 0 {
  5. return true
  6. }
  7. }
  8. return false
  9. }

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

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

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

  1. if anyPositive() { ... }

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

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

  1. // anyPostive indicates if any value is greater than zero.
  2. func anyPositive(first int, rest ...int) bool {
  3. if first > 0 {
  4. return true
  5. }
  6. for _, v := range rest {
  7. if v > 0 {
  8. return true
  9. }
  10. }
  11. return false
  12. }

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