go使用options模式设置参数

Functional Options Pattern(函数式选项模式)可用于传递不同选项配置到方法中。当每次选项参数有变更的时候,可以不改变方法或者接口的参数,这样保证了接口兼容性。使用Options的模式也更利于程序的扩展,所以在众多包中得到了广泛的使用。

下面我来具体讲解一下Options的使用方法:

1.  使用函数传递传递参数

一般我们需要创建一个服务的客户端,可能是这样的写的

type Client struct {
   url      string
   username string
   password string
}

func NewClient(url string, username string, password string,db string) *Client {
   return &Client{
      url:      url,
      username: username,
      password: password
   }
}

这么写的时候,参数都是通过New方法进行传入的。后来因为需求,又需要添加如下可选参数

readTimeout time.Duration    // 读取的超时时间
charset string               // 可以配置编码

如果按参数传递的话,我们就得修改New方法的参数了,如果有多个项目依赖这个包的话,那么引用的地方都得修改相应的New方法参数。那么怎么很好的解决这个问题,这个时候Options模型就出现了。

2. 使用options选项模式

我们首先将上述的可选参数以options方式进行定义(这里options为小写,是不对对外暴露出来具体的配置项)

type options struct { 
    readTimeout time.Duration 
    charset string 
}

然后我们定义Option的结构

type Option func(*options)

接着我们再定义配置options中参数的公开方法,用来设置参数

func WithReadTimeout(timeout time.Duration) Option {
   return func(o *options) {
      o.readTimeout = timeout
   }
}

func WithCharset(charset string) Option {
   return func(o *options) {
      o.charset = charset
   }
}

同时将options添加到Client中,并在New方法中添加相应的处理

type Client struct {
   url      string
   username string
   password string
   opts   options
}

func NewClient(url string, username string, password string,opts ...Option) *Client {
   // 创建一个默认的options
   op := options{
      readTimeout: 10,
      charset: "utf8",
   }
   // 调用动态传入的参数进行设置值
   for _, option := range opts {
      option(&op)
   }
   return &Client{
      url:      url,
      username: username,
      password: password,
      opts:    op,
   }
}

使用Options的模式后,添加参数就不需要再去修改New方法了。 添加参数我们只需要在options中添加对应的属性即可,同时配置相应的对外的WithXXX方法即可。

于是我们调用就可以使用如下方式

client := NewClient("localhost:3333","test","test",WithReadTimeout(100),WithCharset("gbk"))

后面的可以选参数可以传入任意多个Option了。这里配置的Option参数多了后,导致配置过长,我们进一步把他封装到函数中

func Options() []Option{
    return []{
        WithReadTimeout(100),
        WithCharset("gbk")
    }
}

client := NewClient("localhost:3333","test","test",Options()...)

到这里options选项模型就可以很方便的传递可选参数了,同时也不需要添加一个参数就去添加方法的参数。

3. 我们来看看其他的一些options的用户

   3.1.  gRPC中也大量使用使用options选项模式传递参数

下面是Dial方法使用options选项模式

type dialOptions struct {
   unaryInt  UnaryClientInterceptor
   streamInt StreamClientInterceptor

   chainUnaryInts  []UnaryClientInterceptor
   chainStreamInts []StreamClientInterceptor

   cp              Compressor
   dc              Decompressor
   bs              internalbackoff.Strategy
   block           bool
   returnLastError bool
   timeout         time.Duration
   scChan          <-chan ServiceConfig
   authority       string
   copts           transport.ConnectOptions
   callOptions     []CallOption
   balancerBuilder             balancer.Builder
   channelzParentID            int64
   disableServiceConfig        bool
   disableRetry                bool
   disableHealthCheck          bool
   healthCheckFunc             internal.HealthChecker
   minConnectTimeout           func() time.Duration
   defaultServiceConfig        *ServiceConfig // defaultServiceConfig is parsed from defaultServiceConfigRawJSON.
   defaultServiceConfigRawJSON *string
   resolvers                   []resolver.Builder
}

// options处理的交给实现接口类
type DialOption interface {
   apply(*dialOptions)
}

func Dial(target string, opts ...DialOption) (*ClientConn, error) {
   return DialContext(context.Background(), target, opts...)
}

---------- 具体实现apply的实现 ----------------
type funcDialOption struct {
   f func(*dialOptions)
}

func (fdo *funcDialOption) apply(do *dialOptions) {
   fdo.f(do)
}

func WithTimeout(d time.Duration) DialOption {
   return newFuncDialOption(func(o *dialOptions) {
      o.timeout = d
   })
}

上面我可以看到grpc的dial方法是另外一种实现options的方式,同时dialOptions里面还封装了其他的options,其中比较有意思是callOptions,它是一个CallOption类型数组,而CallOption是一个interface的类型,如下:

type CallOption interface {

	before(*callInfo) error

	after(*callInfo, *csAttempt)
}

这个有点像AOP一样的,为call调用之前和之后提供了前置和后置的回调。

3.2 还有其他很多框架中也大量使用options模式

   1. bilibili开源框架kratos: https://github.com/go-kratos/kratos/blob/main/options.go

   2. beego框架封装的httpclient: beego/httpclient.go at develop · beego/beego · GitHub

   3. etc...

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿CKeen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值