乐趣区

关于golang:GOFunctional-Options

在咱们编程中,咱们会经常性的须要对一个对象(或是业务实体)进行相干的配置。比方上面这个业务实体(留神,这仅只是一个示例):

type Server struct {
    Addr     string
    Port     int
    Protocol string
    Timeout  time.Duration
    MaxConns int
    TLS      *tls.Config
}

在这个 Server 对象中,咱们能够看到:
要有侦听的 IP 地址 Addr 和端口号 Port,这两个配置选项是必填的(当然,IP 地址和端口号都能够有默认值,当这里咱们用于举例认为是没有默认值,而且不能为空,须要必填的)。
而后,还有协定 ProtocolTimeoutMaxConns 字段,这几个字段是不能为空的,然而有默认值的,比方:协定是 tcp, 超时 30 秒 和 最大链接数 1024 个。
还有一个 TLS 这个是平安链接,须要配置相干的证书和私钥。这个是能够为空的。

要解决这个问题,最常见的形式是应用一个配置对象,如下所示:

type Config struct {
    Protocol string
    Timeout  time.Duration
    Maxconns int
    TLS      *tls.Config
}

咱们把那些非必输的选项都移到一个构造体里,于是 Server 对象变成了:

type Server struct {
    Addr string
    Port int
    Conf *Config
}

Functional Options

首先,咱们先定义一个函数类型:

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}
}

下面这组代码传入一个参数,而后返回一个函数,返回的这个函数会设置本人的 Server 参数。例如:

当咱们调用其中的一个函数用 MaxConns(30)
其返回值是一个 func(s* Server) {s.MaxConns = 30} 的函数。
这个叫高阶函数。在数学上,就如同这样的数学定义,计算长方形面积的公式为:rect(width, height) = width * height; 这个函数须要两个参数,咱们包装一下,就能够变成计算正方形面积的公式:square(width) = rect(width, width) 也就是说,squre(width)返回了另外一个函数,这个函数就是 rect(w,h) 只不过他的两个参数是一样的。即:f(x) = g(x, x)

好了,当初咱们再定一个 NewServer()的函数,其中,有一个可变参数 options 其能够传出多个下面上的函数,而后应用一个 for-loop 来设置咱们的 Server 对象。

func NewServer(addr string, port int, options ...func(*Server)) (*Server, error) {

  srv := Server{
    Addr:     addr,
    Port:     port,
    Protocol: "tcp",
    Timeout:  30 * time.Second,
    MaxConns: 1000,
    TLS:      nil,
  }
  for _, option := range options {option(&srv)
  }
  //...
  return &srv, nil
}

于是,咱们在创立 Server 对象的时候,咱们就能够这样来了。

s1, _ := NewServer("localhost", 1024)
s2, _ := NewServer("localhost", 2048, Protocol("udp"))
s3, _ := NewServer("0.0.0.0", 8080, Timeout(300*time.Second), MaxConns(1000))

高度的整洁和优雅, 岂但解决了应用 Config 对象形式 的须要有一个 config 参数,但在不须要的时候,是放 nil 还是放 Config{}的抉择艰难,也不须要援用一个 Builder 的管制对象,间接应用函数式编程的试,在代码浏览上也很优雅。

所以,当前,大家在要玩相似的代码时,强烈推荐应用 Functional Options 这种形式,这种形式至多带来了如下的益处:

  • 直觉式的编程
  • 高度的可配置化
  • 很容易保护和扩大
  • 自文档
  • 对于新来的人很容易上手
  • 没有什么令人困惑的事(是nil 还是空)

参考资料

GO 编程模式:FUNCTIONAL OPTIONS

退出移动版