场景:假设你写的一个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