Go 言语完成可选参数:重载?变长参数?

嗨,大家好!本文是系列文章 Go 小技巧第八篇。系列文章检查:Go 言语小技巧

咱们编程时,常会遇到:一个函数在大多数情况下只需求几个参数,但偶然也需求一些不固定的选项参数。在一些言语中,经过重载或许可选参数来解决这个问题。但 Go 中,情况有所不同,因为 Go 不支撑函数重载,也没有内置可选参数功能。假如就想要这样的能力,如安在 Go 中完成?

本文将基于这个主题打开,一步步介绍 GO 中完成可选参数的几种办法。

办法1:可变长参数(Variadic Args)

GO 不支撑可选参数,但它好在仍是支撑可变长参数,即允许函数承受恣意数量的参数。这是经过在参数类型前加上 ... 来完成的。

示例代码,如下所示:

func printNumbers(numbers ...int) {
    for _, number := range numbers {
        fmt.Println(number)
    }
}
func main() {
    printNumbers(1, 2)
    printNumbers(1, 2, 3, 4)
}

在上面的比如中,咱们界说了一个 printNumbers 函数,它能够承受恣意数量的整数作为参数。

这种办法主要仍是适合于一切参数都是同一类型的情况。

Go 言语完成可选参数:重载?变长参数?

但假如参数类型不同怎么办呢?

当然,一种办法是,经过运用 ...interface{} 继续基于可变长参数完成,但这毫无疑问会增加反射或许类型挑选或推导的开支,一起每个位置的参数按索引确定,代码复杂度必定提高,可读性会大大降低,

那么,是否还有更好的办法呢?

办法2:运用Map

当你需求传递不确定数量且类型不同的参数时,能够运用 map 完成。

func setConfig(configs map[string]interface{}) {
    if val, ok := configs["timeout"]; ok {
        fmt.Println("Timeout:", val)
    }
    if val, ok := configs["path"]; ok {
        fmt.Println("Path:", val)
    }
}
func main() {
    setConfig(map[string]interface{}{
        "timeout": 30,
        "path":    "/usr/bin",
    })
}

在这个比如中,setConfig 函数承受一个 map 作为参数,其中键是装备项的名称,值是装备项的值。

Go 言语完成可选参数:重载?变长参数?

这种办法的缺陷是失去了类型安全性,也需求在运行时对 interface{} 类型参数进行类型断言,仅仅相对于变长参数的办法,类型相对比较明确。

有没有不会失去类型安全的办法呢?

办法3:运用结构体(Structs)

假如咱们想要类型安全,一起又想要可选参数的灵活性,结构体好像是一个不错的挑选。但每次调用函数时都需求创立一个新的结构体实例,这会不会太麻烦?

type Config struct {
    Timeout int
    Path    string
}
func setConfig(config Config) {
    fmt.Println("Timeout:", config.Timeout)
    fmt.Println("Path:", config.Path)
}
func main() {
    setConfig(Config{
        Timeout: 30,
        Path:    "/usr/bin",
    })
}

这种办法的好处是类型安全,而且能够明晰地看到哪些参数被设置了。

Go 言语完成可选参数:重载?变长参数?

缺陷是每次调用函数时都需求创立一个新的结构体实例。

办法4:函数选项形式(Functional Options Pattern)

那么,有没有一种办法既能坚持类型安全,又能提供灵活的可选参数呢?函数选项形式好像提供了这样的可能。

type Config struct {
    Timeout int
    Path    string
}
type Option func(*Config)
func WithTimeout(timeout int) Option {
    return func(c *Config) {
        c.Timeout = timeout
    }
}
func WithPath(path string) Option {
    return func(c *Config) {
        c.Path = path
    }
}
func NewConfig(opts ...Option) *Config {
    config := &Config{}
    for _, opt := range opts {
        opt(config)
    }
    return config
}
func main() {
    config := NewConfig(
        WithTimeout(30),
        WithPath("/usr/bin"),
    )
    fmt.Println(config)
}

在这个比如中,咱们界说了Config 结构体和 Option 类型,Option 是一个函数,它承受一个*Config参数。

Go 言语完成可选参数:重载?变长参数?

咱们还界说了WithTimeoutWithPath函数,它们回来一个Option。这样,咱们就能够在调用NewConfig函数时,经过传递不同的选项修正 Config 结构中的字段,构建不同的装备。

这种办法的好处是十分灵活,而且能够在不损坏现有代码的情况下扩展 API。缺陷是完成起来比较复杂,可能需求一些时间来理解。

总结

这篇博文介绍了在Go言语中完成可选参数的几种办法:可变长参数、运用Map、结构体和函数选项形式。每种办法都有其适用场景和优缺陷,你能够根据自己的需求挑选合适的办法。

博文地址:Go 言语完成可选参数:重载?变长参数?