Golang Option模式
1. 传统的结构体初始化方式
type Server struct {
maxConn int
id string
tls bool
}
// NewServer 初始化Server
func NewServer(maxConn int, id string, tls bool) *Server {
return &Server{
maxConn: maxConn,
id: id,
tls: tls,
}
}
func TestServer() {
// 传入配置,初始化Server
server := NewServer(1, "Sakura01", false)
}
这样做的问题在于,如果有一个库或者 Server 有更多的配置项,这样初始化机构体会变得非常繁琐
2. 稍微改进一下
// 创建一个ServerOps包含Server所有配置选项
type ServerOps struct {
maxConn int
id string
tls bool
}
// 将ServerOps嵌入Server中
type Server struct {
ServerOps
}
// NewServer 初始化Server的时候参数为ServerOps,也及时要传入结构体
func NewServer(serverOps ServerOps) *Server {
return &Server{
ServerOps: serverOps,
}
}
func InitServer() {
// 传入配置,初始化Server
server := NewServer(ServerOps{})
fmt.Println(server)
}
可以看到初始化 Server 更麻烦了,不过目前还没有配置完
3. 进一步改进
用户不一定要提供选项才能创建 Server,但是要提供能修改配置项的可能性
所以需要有一个默认配置项DefaultConfig
- 首先定义一个函数类型,函数接收一个指向 ServerOps 的指针
type ServerOpsFunc func(*ServerOps)
// 定义的函数类型有两个使用方式
// 1.返回这个类型的函数
func Test(num int) ServerOpsFunc {
return func(ops *ServerOps) {
ops.maxConn = NUm
}
}
// 2.定义这种类型的函数
func withTls(ServeOps *ServerOps) {
ServeOps.tls = true
}
- 然后创建一个默认配置项
defaultServerOps
函数
func defaultServerOps() ServerOps {
return ServerOps{
maxConn: 10,
id: "dafault",
tls: false,
}
}
- 将初始化 Server 构造器的接收参数改为接收任意个
ServerOpsFunc
函数切片
这样可以使得用户在不提供任何选项的情况下初始化一个配置
// NewServer 初始化Server
func NewServer(serverOps ...ServerOpsFunc) *Server {
defaultOps := defaultServerOps()
// 如果提供了配置项,那么
for _, fn := range serverOps {
fn(&defaultOps)
}
return &Server{
ServerOps: defaultOps,
}
}
- 如果在NewServer 中提供了配置项,那么遍历参数列表,修改 defaultOps 的值
func withMaxConn(n int) ServerOpsFunc {
return func(ops *ServerOps) {
ops.maxConn = n
}
}
func withId(id string) ServerOpsFunc {
return func(ops *ServerOps) {
ops.id = id
}
}
// withTLs可以有两种写法,这两种写法都实现了自定义的函数类型
func withTls() ServerOpsFunc {
return func(ops *ServerOps) {
ops.tls = true
}
}
func withTls(ServeOps *ServerOps) {
ServeOps.tls = true
}
其中 wirhTls 有两种写法,既可以返回一个 ServerOpsFunc,也可以直接将函数定义为 SeverOpsFunc
func InitServer() {
// 默认配置
server := NewServer()
fmt.Println("---------默认配置--------")
fmt.Println(server)
// 传入配置项
serve1 := NewServer(withId("SakurasServer"), withMaxConn(999))
fmt.Println("---------传入配置--------")
fmt.Println(serve1)
// withTLS的两种使用
// func withTls() ServerOpsFunc
serve2 := NewServer(withTls())
fmt.Println("--------TLS配置--------")
fmt.Println(serve2)
// func withTls(ServeOps *ServerOps)
// server4 := NewServer(withTls)
}
4. 完整代码
package Option
import (
"fmt"
)
type ServerOpsFunc func(*ServerOps)
type ServerOps struct {
maxConn int
id string
tls bool
}
func defaultServerOps() ServerOps {
return ServerOps{
maxConn: 10,
id: "dafault",
tls: false,
}
}
type Server struct {
ServerOps
}
func withMaxConn(n int) ServerOpsFunc {
return func(ops *ServerOps) {
ops.maxConn = n
}
}
func withId(id string) ServerOpsFunc {
return func(ops *ServerOps) {
ops.id = id
}
}
// withTLs可以有两种写法,这两种写法都实现了自定义的函数类型
func withTls() ServerOpsFunc {
return func(ops *ServerOps) {
ops.tls = true
}
}
func withTls2(ServeOps *ServerOps) {
ServeOps.tls = true
}
// NewServer 初始化Server
func NewServer(serverOps ...ServerOpsFunc) *Server {
defaultOps := defaultServerOps()
// 如果提供了配置项,那么挨个比那里修改默认配置
// 因为切片是函数类型的,所以fn为函数
// 这里不明白的可以debug一下
for _, fn := range serverOps {
fn(&defaultOps)
}
return &Server{
ServerOps: defaultOps,
}
}
func InitServer() {
// 默认配置
server := NewServer()
fmt.Println(server)
// 自定义配置
server1 := NewServer(withId("Sakura"), withMaxConn(1000))
fmt.Println(server1)
}
可以看到自定义配置中,没有提供的配置项,用的还是默认配置