在日常開發中,我們有時候需要使用默認設置,但有時候需要提供自定義設置 結構體/類,在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
}
這樣利用閉包的特性,當我們需要額外添加參數時,只需要增加配置選項函數即可,拓展性很強。