需求
Golang不支持在函数定义的时候声明默认值,也就是说我们不能够使用类似下面的代码
1 2 3
| func New(addr string="localhost", port string="8080"){ ... }
|
并且也不支持函数的重载,函数名不能够重复,无法根据需要的参数数量创建不同的函数形式,也就是我们不能使用下面的方式
1 2 3 4 5 6 7
| func New(addr string){ ... }
func New(addr string, port string){ ... }
|
返回默认对象
那么我们如何才能设置默认值呢?一种很自然的方式比如说,我们创建对象的时候就返回一个带有默认值的对象
1 2 3 4 5 6 7 8 9 10 11 12
| type myStruct struct { Host string Port string }
func New() *myStruct{ return &myStruct{ Host: "0.0.0.0", Port: "8080", } }
|
然后我们如果需要修改参数,那么我们可以手动去修改
1 2 3
| config := New()
config.Port = "8000"
|
上面的这种方式其实已经可以解决我们的需求,但是还是不够好,因为这种方式会直接暴露
结构体中的字段,结构体中的字段名首字母必须大写,并且用户进行修改的时候会直接对字段进行修改,无法对设置的参数进行校验。
Option模式
下面我们主要讲解 Option模式
,也就是 函数选项模式
,这种模式下在我们设置默认值的同时支持检验参数。
Option 模式为 Golang 的开发者提供了将一个函数的参数设置为可选的功能,也就是说我们可以选择参数中的某几个,并且可以按任意顺序传入参数。
下面我们来看看具体如何使用:
首先,我们需要设置一个结构体包括我们需要设置的一些参数
1 2 3 4
| type Config struct{ Addr string Host string }
|
然后,我们需要定义一个Option
函数类型可以修改我们的参数结构体
1
| type Option func(c *Config)
|
对于我们需要进行修改的参数,我们可以声明对应的函数来修改参数值,这里利用了闭包的特性。
1 2 3 4 5 6 7 8 9 10 11
| func WithHost(host string) Option { return func(c *Config) { c.Host = host } }
func WithPort(port string) Option { return func(c *Config) { c.Port = port } }
|
接着,我们需要设置一个默认值,如下,也就是用户没有设置任何参数的时候所得到的值
1 2 3 4
| var defaultConfig = Config{ Host: "0.0.0.0", Port: "8080", }
|
最后,提供一个函数接口,创建对象的时候,用户可以执行一系列的Option
来修改默认的参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| type myStruct struct { host string port string }
func New(opts ...Option) *myStruct { config := defaultConfig
for _, opt := range opts { opt(&config) }
return &myStruct{ host: config.Host, port: config.Port, } }
|
这里还有另外一种方式实现,思路差不多,直接上代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| type myStruct struct { host string port string }
type Option interface { apply(*myStruct) }
type optinFunc func(*myStruct)
func (f optinFunc) apply(m *myStruct) { f(m) }
func WithHost(host string) Option { return optinFunc(func(ms *myStruct) { ms.host = host }) }
func WithPort(port string) Option { return optinFunc(func(ms *myStruct) { ms.port = port }) }
var defaultOne = myStruct{ host: "0.0.0.0", port: "8080", }
func New(options ...Option) *myStruct { m := defaultOne for _, opt := range options { opt.apply(&m) } return &m }
|
测试结果
1 2 3 4 5 6 7
| fmt.Printf("%#v\n", New()) fmt.Printf("%#v\n", New(WithHost("127.0.0.1"))) fmt.Printf("%#v\n", New(WithHost("127.0.0.1"), WithPort("8000")))
|
虽然说,Option模式在设置参数的时候特别有用,而且显得十分优雅,但是通过对它的实现,我们也知道使用起来相对而言还是比较麻烦,需要添加很多函数