在日常开发中,我们有时候需要使用默认设置,但有时候需要提供自定义设置 结构体/类,在Java我们可以使用无参、有参构造函数来实现,在PHP中我们也可以使用构造函数来实现(如 public function __construct($isCName = false, $securityToken = NULL, $requestProxy = NULL))。但在golang中无法这样做,不过我们可以使用另外一种方式优雅的实现。
1.举例
在这之前,我们在golang中大多是使用以下方式来实现的:
type ExampleClient struct {
Name string
Job int
}
func NewExampleClient(name, job string) ExampleClient {
if name == "" {
name = "default"
}
if job == "" {
job = "default"
}
return ExampleClient{
Name: name,
Job: job,
}
}
这种方式侵入性比较强,如果此时我们需要增加一个超时参数或其他更多参数,那么需要在原代码基础上做很多的修改。
2.实现默认参数
在使用go-micro的过程中,发现其初始化服务配置的方式如下👇
func main() {
sr := micro.NewService()
//或
sr := micro.NewService(
micro.Name("xxx.xxx.xxx"),
micro.Address("192.168.1.1"),
)
}
进入到micro.NewService
中,可以看到在初始化的过程中都是以type Option func(*Options)
类型的函数作为参数,并调用newOptions
方法👇
type Option func(*Options)
// NewService creates and returns a new Service based on the packages within.
func NewService(opts ...Option) Service {
return newService(opts...)
}
func newService(opts ...Option) Service {
options := newOptions(opts...)
// service name
serviceName := options.Server.Options().Name
// wrap client to inject From-Service header on any calls
options.Client = wrapper.FromService(serviceName, options.Client)
return &service{
opts: options,
}
}
我们再接着进入到micro.newOptions
中查看👇
type Options struct {
Broker broker.Broker
Registry registry.Registry
...
}
func newOptions(opts ...Option) Options {
opt := Options{
Broker: broker.DefaultBroker,
Registry: registry.DefaultRegistry,
...
}
for _, o := range opts {
o(&opt)
}
return opt
}
// Name of the service
func Name(n string) Option {
return func(o *Options) {
o.Server.Init(server.Name(n))
}
}
// Address sets the address of the server
func Address(addr string) Option {
return func(o *Options) {
o.Server.Init(server.Address(addr))
}
}
现在,我们知道了如何实现函数默认参数,最重要的步骤如下👇
//定义结构体
type ExampleClient struct {
Name string
Job string
}
//定义配置选项函数(关键)
type Option func(*ExampleClient)
func SetName(name string) Option {// 返回一个Option类型的函数(闭包):接受ExampleClient类型指针参数并修改之
return func(this *ExampleClient) {
this.Name = name
}
}
//应用函数选项配置
func NewExampleClient(opts ...Option) ExampleClient{
// 初始化默认值
defaultClient := ExampleClient{
Name: "default",
Job: "default",
}
// 依次调用opts函数列表中的函数,为结构体成员赋值
for _, o := range opts {
o(&defaultClient)
}
return defaultClient
}
这样利用闭包的特性,当我们需要额外添加参数时,只需要增加配置选项函数即可,拓展性很强。