Go默认参数实现

Functional Options Pattern

在开发过程中,为了避免繁琐的参数设置,难免会需要将函数的一些参数设置为默认值,函数或对象使用这些默认值来保证开箱即用。

然而Go语言本身并不支持默认参数。之前看go-micro源码时,发现它的代码实现中有一种优雅的默认参数实现方式。搜了一下这种实现方式,发现也并不是go-micro的首创,而是基于一种go的设计模式:Functional Options Pattern(函数式选项模式)。恰好最近写代码也用这种模式尝试了下默认参数设置,所以趁此机会总结一下。

Options

为了方便对照,这里还是使用go-micro中的源码来演示。 首先还是将需要的参数构造为一个结构体Options

type Options struct {
    Codecs       map[string]codec.NewCodec
    Broker       broker.Broker
    Registry     registry.Registry
    Tracer       trace.Tracer
    Auth         auth.Auth
    Transport    transport.Transport
    Metadata     map[string]string
    Name         string
    Address      string
    Advertise    string
    Id           string
    Namespace    string
    Version      string
    ...
    ...
    ...
}

Option

然后定义一个以Options为参数的函数Option,方便后续我们对结构体中的参数进行修改。

type Option func(*Options)

func

实现需要指定结构体参数的函数,返回为Option

// Server name
func Name(n string) Option {
    return func(o *Options) {
        o.Name = n
    }
}

// Namespace to register handlers in
func Namespace(n string) Option {
    return func(o *Options) {
        o.Namespace = n
    }
}

// Unique server id
func Id(id string) Option {
    return func(o *Options) {
        o.Id = id
    }
}

// Version of the service
func Version(v string) Option {
    return func(o *Options) {
        o.Version = v
    }
}

newOptions

当我们创建Option时,若指定了结构体中的参数,则使用指定参数,否则使用默认参数。

func newOptions(opt ...Option) Options {
    opts := Options{
        Codecs:           make(map[string]codec.NewCodec),
        Metadata:         map[string]string{},
        RegisterInterval: DefaultRegisterInterval,
        RegisterTTL:      DefaultRegisterTTL,
    }

    for _, o := range opt {
        o(&opts)
    }

    ...
    ...
    ...

    if len(opts.Name) == 0 {
        opts.Name = DefaultName
    }

    if len(opts.Id) == 0 {
        opts.Id = DefaultId
    }

    if len(opts.Version) == 0 {
        opts.Version = DefaultVersion
    }

    return opts
}

在这段代码:

    for _, o := range opt {
        o(&opts)
    }

中,通过一系列之前构建的以Options结构为参数的函数,来实现对Option的参数指定。可以说,newOptions函数中除了这段for循环代码外,其他都是对Options结构的默认参数设置。

Server

让我们来对上面实现的参数设置进行调用。

func newRpcServer(opts ...Option) Server {
    options := newOptions(opts...)
    ...
    ...
    ...

    return &rpcServer{
        opts:        options,
        ...
        ...
        ...
    }
}

调用:

defaultServer := newRpcServer()                     // 全部使用默认配置
nameServer := newRpcServer(Name("Test"))            // 指定Server name为Test,其他使用默认配置
mulServer := newRpcServer(Name("Test"), Id("1234)
        Version("1.0.1"))                            // 指定多个参数,其他使用默认配置
Copyright © itrunner.cn 2020 all right reserved,powered by Gitbook该文章修订时间: 2020-07-07 21:58:31

results matching ""

    No results matching ""