Golang编程模式(一) Functional Options
应用场景举例:
在编程中,我们经常需要对一个对象(或是业务实体)进行相关的配置。比如下面这个业务实体
1 | type Server struct { |
在这个 Server 对象中,我们可以看到:
要有侦听的 IP 地址 Addr 和端口号 Port ,这两个配置选项是必填的
还有协议 Protocol 、 Timeout 和MaxConns 字段,这几个字段是不能为空的,但是有默认值的,比如,协议是 TCP,超时30秒 和 最大链接数1024个
还有一个 TLS ,这个是安全链接,需要配置相关的证书和私钥。这个是可以为空的。
针对这样的配置,需要多种不同的函数签名创建不同配置的Server,例如:
1 | func NewDefaultServer(addr string, port int) (*Server, error) { |
就很蠢🤔
有三种解决办法
第一种比较简单但不推荐:引入一个Config{},将非必填的字段放到Config里,传入时按需赋值字段(也可以传入空struct,所以Server函数中要有相应的判空操作) ### Builder设计模式
(java开发中较常见)
第二种方法引入一个ServerBuilder{}
1 |
|
通过组合式编程(链式调用),自由组合非必填参数:
1 | sb := ServerBuilder{} |
函数式编程
golang style
首先定义一个函数类型变量
1 | type Option func(*Server) |
然后,使用函数式编程定义以下一组函数
1 | func Protocol(p string) Option { |
这组函数传入相应字段参数,返回一个用于设置*Server成员的匿名函数
例如调用MaxConns(30)时,返回值为匿名函数:
1 | func(s *Server){ |
接下来就可以定义一个初始化Server对象的函数NewServer(),入参中包含一个可变参数options,可以传入多个上面的函数参数,然后使用一个for-loop来设置Server对象。
1 |
|
于是,创建一个Server对象时,使用下面这样的代码:
1 | s1, _ := NewServer("localhost", 1024) |
使用以上方式,不需引入Config{}或者使用Builder,代码优雅,提高了代码的可扩展性和可维护性,类似于可插拔式中间件的设计带来了高度的可配置化,使新使用者易于上手。