近期在查閱go-micro源碼過程當中發現,在設置建立微服務的參數選項時都依賴於項目外層一個名爲
github.com/micro/go-micro/options.go
的文件,它定義了建立一個微服務所須要的各類參數選項,其實現過程當中使用的方法值得借鑑。
如go-micro 框架初探篇,在go-micro中使用micro.NewService建立一個微服務html
import "github.com/micro/go-micro" service := micro.NewService()
也能夠在建立過程當中設置服務選項(如服務名稱,自定義服務發現等)git
service := micro.NewService( micro.Name("hellooo"), micro.Version("latest"), )
對比兩個例子能夠發現,雖然go語言不支持默認參數,可是上面的代碼例子卻達到了一樣的效果。github
那麼,go-micro是如何實現默認參數的呢?進入micro.NewService
源碼看個究竟:github.com/micro/go-micro/go-micro.go
segmentfault
// Option定義爲一個方法,接受Options類型的指針參數 // 注意這個Option類型(函數類型),這是理解本文的關鍵 type Option func(*Options) ... // 建立並返回一個服務:接收Option函數類型的不定向參數列表 func NewService(opts ...Option) Service { return newService(opts...) } ... // 內部真正建立服務的方法 func newService(opts ...Option) Service { // 關鍵步驟:利用opts函數列表初始化服務 options := newOptions(opts...) ... // 返回初始化的服務 return &service{ opts: options, } }
其中newOptions
方法最爲關鍵,繼續跟蹤下去:github.com/micro/go-micro/options.go
,該文件定義了設置服務相關的全部選項:api
// 服務選項結構體 // 由於go-micro是可插件化的框架,其中的組件(如消息中間件)均是能夠用其餘相似服務替換的 type Options struct { Broker broker.Broker Cmd cmd.Cmd Client client.Client Server server.Server Registry registry.Registry Transport transport.Transport // Register loop interval RegisterInterval time.Duration // Before and After funcs BeforeStart []func() error BeforeStop []func() error AfterStart []func() error AfterStop []func() error Context context.Context } // 生成服務相關選項(初始化使用go-micro框架定義的默認組件) func newOptions(opts ...Option) Options { // 初始化默認值 opt := Options{ Broker: broker.DefaultBroker, Cmd: cmd.DefaultCmd, Client: client.DefaultClient, Server: server.DefaultServer, Registry: registry.DefaultRegistry, Transport: transport.DefaultTransport, Context: context.Background(), } for _, o := range opts { o(&opt) // 依次調用opts函數列表中的函數,爲服務選項(opt變量)賦值 } return opt } ... // 服務名字選項 // 返回一個Option類型的函數(閉包):接受Options類型指針參數並修改之 func Name(n string) Option { return func(o *Options) { o.Server.Init(server.Name(n)) } } // 服務版本選項 // 返回一個Option類型的函數(閉包):接受Options類型指針參數並修改之 func Version(v string) Option { return func(o *Options) { o.Server.Init(server.Version(v)) } } ...
除了Name與Version選項以外,go-micro的Broker、Register、Transport等選項均是經過此方法實現的。它極大地利用了go語言支持閉包的特性,優雅地實現了函數支持默認參數
的功能。
再次回到文章開頭出建立服務的代碼就很好理解了:閉包
// 建立服務,同時接收對指定參數選項的設置 service := micro.NewService( micro.Name("hellooo"), micro.Version("latest"), )
這樣的寫法處理調用代碼比較簡潔以外,它擴展性特別好,增長新的選項時,只須要不多量的代碼。框架
這即是go語言的函數選項模式。函數
函數選項模式是由Rob Pike提出,並由Dave Cheney等推廣開,它優雅地解決了go語言中默認參數問題。微服務
雖然Functional Options並非一個新的概念,從Rob Pike提出至今已4年有餘(2014),但做爲Golang新手,仍是很受啓發。這也是閱讀源碼帶來的好處。oop
下面總結一下,函數選項模式有哪些優勢:
推而廣之:相似結構體中變量的賦值均可以效仿之。
https://commandcenter.blogspo...
https://dave.cheney.net/2014/...
https://halls-of-valhalla.org...
https://lingchao.xin/post/fun...