相信很多Go开发者都遇到过一个问题,就是Go并不支持函数的可选参数或者参数的默认值,比如:
type stuffClient struct {
conn Connection
timeout int
retries int
}
func NewStuffClient(conn Connection, timeout, retries int) StuffClient {
return &stuffClient{
conn: conn,
timeout: timeout,
retries: retries,
}
}
在调用 NewStuffClient 时必须传递 conn, timeout, retries 这3个参数,但是大多数情况下可能希望使用的是默认值,可惜的是Go并不支持像PHP一样的参数默认值设定以及参数的可选传递。那么在Go中如何优雅的实现这个功能呢?难不成要新建 NewStuffClientWithTimeout、NewStuffClientWithRetries 等不同函数?
Go自然有更好的解决办法,那就是构建options这样的struct,然后通过实现构建好的option函数来延迟对options选项内容进行变更,来达到效果。文字可能有点苍白无力,我们直接来看代码实现
type StuffClientOption func(*StuffClientOptions)
type StuffClientOptions struct {
Retries int //number of times to retry the request before giving up
Timeout int //connection timeout in seconds
}
func WithRetries(r int) StuffClientOption {
return func(o *StuffClientOptions) {
o.Retries = r
}
}
func WithTimeout(t int) StuffClientOption {
return func(o *StuffClientOptions) {
o.Timeout = t
}
}
var defaultStuffClientOptions = StuffClientOptions{
Retries: 3,
Timeout: 2,
}
func NewStuffClient(conn Connection, opts ...StuffClientOption) StuffClient {
options := defaultStuffClientOptions
for _, o := range opts {
o(&options)
}
return &stuffClient{
conn: conn,
timeout: options.Timeout,
retries: options.Retries,
}
}
这样,利用Go的可变数量参数传递StuffClientOption这个函数变量参数,来动态修改默认值的方式,就可以达到这个效果
x := NewStuffClient(Connection{})
fmt.Println(x) // prints &{{} 2 3}
x = NewStuffClient(
Connection{},
WithRetries(1),
)
fmt.Println(x) // prints &{{} 2 1}
x = NewStuffClient(
Connection{},
WithRetries(1),
WithTimeout(1),
)
fmt.Println(x) // prints &{{} 1 1}