在看uber的golang最佳实践时,如果初始化一个结构时,在功能迭代中,初始化的参数不断地增加或改变,怎么才能让初始化结构变得可扩展。 uber提出的解决方案是功能选项。

几种备选方案

横冲直幢型

这是初学者最容易做到的,也是最不理想的,当我们每添加一项,就起一个新的初始化方法

1
2
3
4
5
6
type HttpServer struct{
	Host string
	Port string
}

func NewHttpServer(host, port string) *HttpServer{}

当功能需要我们http服务器能同时支持tls时, 我们可能就换成以下写法

1
2
3
4
5
6
7
8
9
type HttpServer struct{
	Host string
	Port string
	Cert tls.Cert
	Timeout int32
}

func NewHttpServerWithTls(host,port string) *HttpServer{}
func NewHttpServerWithTimeOut(host,port string, timeOut int32) *HttpServer{}

这种写法是没有任何扩展性可言的,当加入更多的配置选项,比如maxConn(最大并发连接数)等,导致针对不同的配置项,都可能产生一种组合,这种方式明显不太可取

没有一个config解决不了的事

显然,上面的方法太不灵活了,只适用于基本不动的配置处理,对于可能新增的选项,修改起来就是灾难。 当然,我们可以用万能的config作参数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
type HttpServer struct{
    Host string
	Config *Config
}

type Config struct {
    Port string
    Cert tls.Config
    Timeout int32
}

func NewHttpServer(host, &Config{
	Port:8888,
})*HttpServer

每次有新的配置加入时,我们可以直接塞进config选项中,这样,我们只需要根据不同的传参来解决扩展性问题。

但当我们以为这是种比较完美的解决方案时,实际上,并没有想的那么美好。

它在默认值方面存在问题,例如,在此处显示的配置结构中,如果未提供 Port,NewServer 将返回 *Server 以侦听端口 8080。

但这有一个缺点,即您无法再显式将 Port 设置为 0 并让操作系统自动选择空闲端口,因为显式 0 与字段的零值无法区分。

更好的方式,功能选项

 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
type Option func(*HttpServer)

func Timeout(timeout time.Duration) Option {
    return func(s *Server) {
        s.Timeout = timeout
    }
}

func MaxConns(maxconns int) Option {
    return func(s *Server) {
        s.MaxConns = maxconns
    }
}

func TLS(tls *tls.Config) Option {
    return func(s *Server) {
        s.TLS = tls
    }
}

func NewHttpServer(host string, options ...func(*Server))*HttpServer{
	httpServer := &NewHttpServer{host}
	
	for _,v := range options{
		v(&httpServer)
    }
}

s  :=  NewHttpServer("localhost")
s1 := NewHttpServer("0.0.0.0", Timeout(300*time.Second), MaxConns(1000))

功能选项能完美的解决以上问题。有不少知名的库也采用该方法初始化

文末有该文章的参考文献,14年就有大佬提出的方案,到现在也毫不过时,不得不佩服。

参考文献

https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis https://github.com/xxjwxc/uber_go_guide_cn#%E5%8A%9F%E8%83%BD%E9%80%89%E9%A1%B9 https://coolshell.cn/articles/21146.html