解析option设计模式
- 一、背景
- 二、应用demo
- 三、Gin中的应用
一、背景
有时候一个函数会有很多参数,为了方便函数的使用,我们会给希望给一些参数设定默认值,调用时只需要传与默认值不同的参数即可。因此选项设计模式顾名思义,就是在构造一个复杂的对象时,能以可选参数(选项)的形式,传入构造对象的函数中,为结构体字段赋值。先直接说下它的应用:
- 需要构造对象比较复杂,有较多的参数需要赋值
- 有默认的参数项,然后其余部分的参数需要通过插拔的形式进行配置
- 以后可能还会有新的参数不断加进来
二、应用demo
现在以一个具体的应用场景来解析下具体怎么构造一个options的设计模式,假如现在业务场景会构造一个配置对象,但是这个配置是有很多默认的配置,只有少部分需要更改。因此可以利用以下的options模式实现对应的业务场景。
type Config struct {param1 stringparam2 stringoption1 stringoption2 string
}// 声明一个函数类型的变量,用于传参
type Option func(config *Config)func InitConfig(opts ...Option) *Config {config := &Config{param1: "default1",param2: "default2",}for _, opt := range opts {opt(config)}return config
}func WithStringOption1(str string) Option {return func(config *Config) {config.option1 = str}
}func WithStringOption2(str string) Option {return func(config *Config) {config.option2 = str}
}func main() {config1 := InitConfig(WithStringOption1("option1"),WithStringOption2("option2"),)config2 := InitConfig(WithStringOption1("option1"),)fmt.Println(*config1)fmt.Println(*config2)
}
- 代码中param1和param2则是默认配置的参数,option1和option2则是需要进行配置的参数。而最关键的则是通过函数式的变量,将不同的赋值操作统一,最终通过闭包赋值给对应构造对象。
type Option func(config *Config)
三、Gin中的应用
gin中最典型的应用options模式的就是在构建engine的时候。
- New(opts …OptionFunc)
func New(opts ...OptionFunc) *Engine {debugPrintWARNINGNew()engine := &Engine{RouterGroup: RouterGroup{Handlers: nil,basePath: "/",root: true,},FuncMap: template.FuncMap{},RedirectTrailingSlash: true,RedirectFixedPath: false,HandleMethodNotAllowed: false,ForwardedByClientIP: true,RemoteIPHeaders: []string{"X-Forwarded-For", "X-Real-IP"},TrustedPlatform: defaultPlatform,UseRawPath: false,RemoveExtraSlash: false,UnescapePathValues: true,MaxMultipartMemory: defaultMultipartMemory,trees: make(methodTrees, 0, 9),delims: render.Delims{Left: "{{", Right: "}}"},secureJSONPrefix: "while(1);",trustedProxies: []string{"0.0.0.0/0", "::/0"},trustedCIDRs: defaultTrustedCIDRs,}engine.RouterGroup.engine = engineengine.pool.New = func() any {return engine.allocateContext(engine.maxParams)}return engine.With(opts...)
}
从代码中可以看出函数中构造了一大堆默认参数,然后在return时返回了engine.With(opts…),继续深入:
type OptionFunc func(*Engine)func (engine *Engine) With(opts ...OptionFunc) *Engine {for _, opt := range opts {opt(engine)}return engine
}
可以发现这里是通过With函数传入函数式参数实现了options模式。在 golang 的很多开源项目里面也用到了选项模式,比如 grpc 中的 rpc 方法就是采用选项模式设计的,除了必填的 rpc 参数外,还可以一些选项参数,grpc_retry 就是通过这个机制实现的,可以实现自动重试功能。