场景:假设你写的一个sdk在用户使用时,希望传入一些可选的参数,那么哪种传参写法更优雅呢?
现在假设你需要写一个创建连接的方法,这个方法包含一些可选参数,参数类型如下:
type options struct {
ip string
port string
username string
password string
}
常见做法
这个时候,这个个option如何传入创建连接的方法就有点小技巧了,常见的做法有以下几种:
- 创建一个构造函数,不想要的参数就传一个空参。
这种写法比较鸡肋,你需要严格按照参数列表和类型进行传参,虽然为空是缺省,但也不友好。
func NewOption(ip, port, username, password string) {
if ip == "" {
}
if port == "" {
}
if username == "" {
}
if password == "" {
}
// do something
}
- 根据常见的可选组合创建多个options的构造函数。然后提供WithXXX的方法。
这种写法还不错,可以选择性的传参,但是传参之前需要构造一个option,然后对option进行一些操作,其实也是有点麻烦的。
func NewOptionDefault() *options {
opt := &options{}
opt.ip = "127.0.0.1"
opt.port = "3306"
opt.username = "root"
opt.password = "root"
// do something
return opt
}
func NewOption(username, password string) *options {
opt := &options{}
opt.ip = "127.0.0.1"
opt.port = "3306"
opt.username = username
opt.password = password
//do something
return opt
}
func (opt * options) WithUsername(username string) {
if username != "" {
opt.username = username
}
}
func (opt * options) WithIP(ipAddr string) {
if ipAddr != "" {
opt.ip = ipAddr
}
}
func NewConnect(opt *Option) {
// do something
}
func main() {
opt := NewOption("atfwus", "atfwus")
opt.WithIP("192.168.1.1")
NewConnect(opt)
}
优雅做法
可以通过golang可选参数的形式,传入Option结构体(结构体中包含一个返回options指针的函数),动态的对参数进行构造。
这种写法在一些sdk中很常见。
package main
type Option struct {
f func(*options)
}
type options struct {
ip string
port string
username string
password string
}
func WithIp(ip string) Option {
return Option{func(op *options){
op.ip = ip
}}
}
func WithPort(port string) Option {
return Option{func(op *options){
op.port = port
}}
}
func WithUserName(username string) Option {
return Option{func(op *options){
op.username = username
}}
}
func WithPassword(password string) Option {
return Option{func(op *options){
op.password = password
}}
}
func NewConnect(ops ...Option) {
// set opt
opt := &options{}
for _, do := range ops {
do.f(opt)
}
// do something
}
func main() {
NewConnect(WithIp("127.0.0.1"), WithPassword("123"))
}
ATFWUS 2022-02-16