本篇继续学习左耳朵耗子老师的Go Programming Patterns下半部分,PPT太长了,70多页。
Topic 10 函数化的选项配置
由于Golang不允许使用同一个名字来命名函数,必须是不同的名称,即使参数不同,这与Java不一样,java的方法签名是包含参数的。所以遇到那种多个参数来实例化一个变量的,就会比较麻烦。会有类似代码产生:
type Server struct {
Addr string
Port int
Protocol string
Timeout time.Duration
MaxConns int
TLS *tls.Config
}
func NewServer(addr string, port int) (*Server, error) {
//...
}
func NewTLSServer(addr string, port int, tls *tls.Config) (*Server, error) {
//...
}
func NewServerWithTimeout(addr string, port int, timeout time.Duration) (*Server, error) { //...
}
func NewTLSServerWithMaxConnAndTimeout(addr string, port int, maxconns int, timeout time.Duration, tls *tls.Config) (*Server, error) {
//...
}
然后就有一种,使用专用配置结构体来优化这种实例化函数。
type Config struct { Protocol string
Timeout time.Duration
Maxconns int
TLS *tls.Config
}
type Server struct {
Addr string
Port int
Conf *Config
}
func NewServer(addr string, port int, conf *Config) (*Server, error) {
//...
}
//Using the default configuratrion
srv1, _ := NewServer("localhost", 9000, nil)
conf := ServerConfig{Protocol:"tcp", Timeout: 60*time.Duration}
srv2, _ := NewServer("locahost", 9000, &conf)
}
这样写有什么问题,这样有一个问题是零值或者nil值的处理,即在NewServer里需要处理这些零值,比如MaxConn要处理是否为0,TLS是否为nil,需要挨个判断,然后处理。然后就有了这种函数化参数的令人眼前一亮的操作。
type Option func(*Server)
func Protocol(p string) Option {
return func(s *Server) {
s.Protocol = p
}
}
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 NewServer(addr string, port int, options ...func(*Server)) (*Server, error) {
srv := Server{
Addr:
Port:
Protocol: "tcp",
Timeout: 30 * time.Second,
MaxConns: 1000,
TLS: nil,
}
for _, option := range options {
option(&srv)
}
//...
return &srv, nil
}
// 使用
s1, _ := NewServer("localhost", 1024)
s2, _ := NewServer("localhost", 2048, Protocol("udp"))
s3, _ := NewServer("0.0.0.0", 8080, Timeout(300*time.Second), MaxConns(1000))
乍一看,有点懵逼是吧。就是把所有的参数,使用了函数来处理,并且返回的是个函数,他通过把参数传递给了要返回的函数。要返回的函数干了啥呢?它把传入的参数,设置到Server这个要初始化的实例上,这样就可以达到在初始化中直接挨个执行这些func,传入sever实例,即可完成初始化。只是我觉得这样做,不如直接给Server写参数函数,直接设置参数值不就好了吗?令人疑惑
func(s *Server) {
s.Timeout = timeout
}
Topic 11 Map/Reduce/Filter.
高阶函数