前言

最近在使用golang 开发时常会碰到一个函数传参问题,就是在使用工厂模式初始化结构体时可能有些字段可传可不传,那我到底该不该把这个字段在函数中初始化呢,所以到底我们应该怎么传呢?


一、interface

可能绝大部分人第一时间想到的就是使用 interface不确定数据类型去实现。效果如下:

type Person struct {
	Name string
	Age int
}

func NewPerson(args ...interface{}) *Person {
	p := new(Person)

	for _, arg := range args {
		switch arg.(type) {
		case string:
			p.Name=arg.(string)
		case int:
			p.Age=arg.(int)
		}
	}
	return p
}

当然这也是一种方法,但是有些时候使用者并不知道需要传什么类型,传什么数据,而且当结构体中的数据类型一致时我们就不知道该怎么去赋值了,所以这类方法也是有点不适当的地方。

二、struct

当然你也可以直接穿一个Person结构体,那这跟你直接new 一个person 结构体有啥区别呢,所以并不是很推荐传入结构体。

三、func(最优解)

既然golang中函数是一等公民的话咱们需要充分利用起来。

// 定义一个函数类型的type
type Option func(person *Person)

// 初始化Person 此时我们入参就不是interface了,而是上面定义的Option
func NewPerson(opts ...Option) *Person  {
	p := new(Person)

	for _, f := range opts {
		f(p)
	}

	return p
}

定义结构体字段的 Option 方法
// WithName ...
func WithName(name string) Option {
	return func(opt *Person) {
		opt.Name = name
	}
}

// WithAge ...
func WithAge(age int) Option {
	return func(opt *Person) {
		opt.Age = age
	}
}

当我们使用时想要初始化啥字段我们就直接传入相应的Option方法就行;而且也不用考虑参数个数,参数类型;代码如下:

func main() {
	person := NewPerson(
		WithName("张三"),
		WithAge(11),
	)
	fmt.Printf("%+v", person) //&{Name:张三 Age:11}
}

此方法是不是让调用者一眼就能看明白,当然开发者需要加字段的话也很简单,只需添加相应的Option方法就行。所以这里还是非常推荐这种方法的。在很多开源项目中也能看到这种模式的身影


总结

看完以上三种方法你是否对golang 的不确定参数的传值到底该怎么传有了答案呢,最后强烈推荐大家使用 第三种方法 使用函数作为参数去 初始化结构体。