配置痛点
- go语言中没有函数重载,所以需要根据不同的可选配置项,需要提供不同的函数签名
type Server struct {
Addr string
Port int
Protocol string
Timeout time.Duration
MaxConns int
TLS *tls.Config
}
func NewDefaultServer(addr string, port int) (*Server, error) {
return &Server{addr, port, "tcp", 30 * time.Second, 100, nil}, nil
}
func NewTLSServer(addr string, port int, tls *tls.Config) (*Server, error) {
return &Server{addr, port, "tcp", 30 * time.Second, 100, tls}, nil
}
func NewServerWithTimeout(addr string, port int, timeout time.Duration) (*Server, error) {
return &Server{addr, port, "tcp", timeout, 100, nil}, nil
}
func NewTLSServerWithMaxConnAndTimeout(addr string, port int, maxconns int, timeout time.Duration, tls *tls.Config) (*Server, error) {
return &Server{addr, port, "tcp", 30 * time.Second, maxconns, tls}, nil
}
封装配置对象
- 把可选配置项单独拆分出来,作为一个结构体参数传入
type Server struct {
Addr string
Port int
}
type Config struct {
Protocol string
Timeout time.Duration
MaxConns int
TLS *tls.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)
- 我们无法知道config是否为nil,需要做一层判断,不是特别优雅
链式调用构造
server.go
type Server struct {
Addr string
Port int
Protocol string
Timeout time.Duration
MaxConns int
TLS *tls.Config
}
//使用一个builder类来做包装
type ServerBuilder struct {
Server
}
func (sb *ServerBuilder) Create(addr string, port int) *ServerBuilder {
sb.Server.Addr = addr
sb.Server.Port = port
//其它代码设置其它成员的默认值
return sb
}
func (sb *ServerBuilder) WithProtocol(protocol string) *ServerBuilder {
sb.Server.Protocol = protocol
return sb
}
func (sb *ServerBuilder) WithMaxConn(maxconn int) *ServerBuilder {
sb.Server.MaxConns = maxconn
return sb
}
func (sb *ServerBuilder) WithTimeOut(timeout time.Duration) *ServerBuilder {
sb.Server.Timeout = timeout
return sb
}
func (sb *ServerBuilder) WithTLS(tls *tls.Config) *ServerBuilder {
sb.Server.TLS = tls
return sb
}
func (sb *ServerBuilder) Build() Server {
return sb.Server
}
main.go
func main() {
sb := functional_options.ServerBuilder{}
server := sb.Create("127.0.0.1", 8080).
WithProtocol("udp").
WithMaxConn(1024).
WithTimeOut(30 * time.Second).
Build()
fmt.Printf("%+v", server)
}
Functional Options
-
Functional Option是Go语言中的一种编程模式,它用于传递可选参数并设置对象的属性。 这种方式在添加属性成员的时候,对代码不会有侵入性
-
先定义函数类型的别名,并且以函数式的方式定义一组函数,利用指针接收者给结构体内部字段传参
option.go
type Option func(*Server)
func Protocol(protocol string) Option {
return func(server *Server) {
server.Protocol = protocol
}
}
func Timeout(timeout time.Duration) Option {
return func(server *Server) {
server.Timeout = timeout
}
}
func MaxConns(maxConns int) Option {
return func(server *Server) {
server.MaxConns = maxConns
}
}
func TLS(tls *tls.Config) Option {
return func(server *Server) {
server.TLS = tls
}
}
- 把函数别名作为可选参数传入构造函数,并且在内部去调用传入的函数去设置属性
server.go
type Server struct {
Addr string
Port int
Protocol string
Timeout time.Duration
MaxConns int
TLS *tls.Config
}
func NewServer(addr string, port int, opts ...Option) (*Server, error) {
s := Server{
Addr: addr,
Port: port,
Protocol: "tcp",
Timeout: 30 * time.Second,
MaxConns: 1000,
TLS: nil,
}
for _, opt := range opts {
opt(&s)
}
//...
return &s, nil
}
- 在外部可以选择传入哪些函数对指定的属性进行赋值,实现优雅的结构体构造器
main.go
func main() {
server1, _ := functional_options.NewServer("localhost", 8080, functional_options.Protocol("udp"))
server2, _ := functional_options.NewServer("localhost", 9090, functional_options.MaxConns(10))
fmt.Printf("%+v, %+v", server1, server2)
}
6 ,最后一个Functional Options写法很gopher