What(什么是Options模式)
- Options模式是Java Builder模式在Go语言中的简易写法
- Options模式得益于Go语言中函数是一等公民,可做为另一个函数的入参和返回
- Options模式用于为某个对象进行相关配置
Why(为什么需要Options模式)
先来看这样一个场景:
我们在业务内部经常会调用其他的API获取数据,为了简化调用,我们做了简单的封装,如下代码所示
type APICall struct {
ip string
port string
}
func NewAPICall(ip string, port string) *APICall {
return &APICall{
ip: ip,
port: port,
}
}
func (a *APICall) Call(reqParam []byte) ([]byte, error) {
//业务逻辑
return nil, nil
}
接下来会这样调用API
func GetToken(ip string, port string, key []byte) ([]byte, error) {
c := NewAPICall(ip,port)
return c.Call(key)
}
现在,我们为APICall增加设置超时时间、日志记录入参和出参的功能,使用方可以选择使用。可能会这样改造APICall
type APICall struct {
ip string
port string
}
func NewAPICall(ip string, port string) *APICall {
return &APICall{
ip: ip,
port: port,
}
}
func (a *APICall) Call(p []byte) ([]byte, error) {
//业务逻辑
return nil, nil
}
func (a *APICall) CallWithTimeout(p []byte,timeout time.Duration) ([]byte, error) {
//业务逻辑+timeout逻辑
return nil, nil
}
func (a *APICall) CallWithLog(p []byte) ([]byte, error) {
//业务逻辑+printlog逻辑
return nil, nil
}
我们发现这样扩展函数的方式有几个问题:
- 当我们增加更多的功能时,我们需要提供更多的函数,同时适用方调用也不方便
- 我们可能需要同时设置超时超时和打印日志,此时我们需要提供的函数数量是笛卡尔积
- 不符合面向对象的思想
从根源上解决这个问题,我们可以将timeout、printlog等做为APICall的属性(符合面向对象的设计),同时利用Options模式,只需要提供一个call函数即可
How(如何使用Options模式)
所谓模式、模型,都是前人总结好的,有着特定《套路》的。
第一步,定义函数类型
type Option func(*APICall)
第二步,给APICall添加属性
type APICall struct {
ip string
port string
timeout time.Duration
printLog bool
}
第三步,定义函数,返回Option
func WithTimeout(t time.Duration) Option {
return func(call *APICall) {
call.timeout = t
}
}
func WithPrintLog() Option {
return func(call *APICall) {
call.printLog = true
}
}
第四步,改造NewAPICall,入参增加Options,同时改造Call函数,适配新增的属性逻辑
func NewAPICall(ip string, port string, options ...Option) *APICall {
instance := &APICall{
ip: ip,
port: port,
}
for _, option := range options {
option(instance)
}
return instance
}
func (a *APICall) Call(p []byte) ([]byte, error) {
//业务逻辑
//timeout逻辑
//printlog逻辑
return nil, nil
}
第五步,调用
func GetToken(ip string, port string, key []byte) ([]byte, error) {
c := NewAPICall(ip, port, WithPrintLog(), WithTimeout(time.Second))
return c.Call(key)
}