Wire是Google提供的幫助Go開發人員實現編譯時依賴注入的工具。
經過例子學習使用Wire。這裏咱們要創建一個小的歡迎程序,用來了解如何使用Wire。git
咱們建立了三種數據類型github
type Message string type Greeter struct { // ... TBD } type Event struct { // ... TBD }
如今,咱們將建立一個簡單的初始化器,老是返回一個硬編碼的消息:shell
func NewMessage() Message { return Message("Hi there!") }
咱們的迎賓員須要引用這條消息,咱們也爲歡迎者建立一個初始化器。小程序
func NewGreeter(m Message) Greeter { return Greeter{Message: m} } type Greeter struct { Message Message // <- adding a Message field }
在初始化器中,咱們給Greeter分配了一個Message字段。如今,當咱們在Greeter上建立一個Greet方法時,咱們可使用這個消息:函數
func (g Greeter) Greet() Message { return g.Message }
接下來,Event須要有一個Greeter,因此咱們也會爲它建立一個初始化器。工具
func NewEvent(g Greeter) Event { return Event{Greeter: g} } type Event struct { Greeter Greeter // <- adding a Greeter field }
而後咱們添加一個方法來啓動事件:學習
func (e Event) Start() { msg := e.Greeter.Greet() fmt.Println(msg) }
Start方法是咱們這個小應用程序的核心:它告訴歡迎者發出一個問候語,而後將該消息打印到屏幕上。測試
如今咱們已經準備好了應用程序的全部組件,讓咱們看看在不使用Wire的狀況下初始化全部組件須要作些什麼。咱們的主函數是這樣的:ui
func main() { message := NewMessage() greeter := NewGreeter(message) event := NewEvent(greeter) event.Start() }
首先咱們建立一個消息,而後用這個消息建立一個歡迎器,最後咱們用這個歡迎器建立一個事件。完成全部初始化以後,咱們就能夠開始事件了。google
依賴項注入的一個缺點是須要如此多的初始化步驟。讓咱們看看如何使用Wire使初始化組件的過程更順暢。
咱們先簡化咱們的主函數:
func main() { e := InitializeEvent() e.Start() }
接下來,在一個名爲wire的單獨文件中。咱們將定義InitializeEvent。這就是事情變得有趣的地方:
// wire.go func InitializeEvent() Event { wire.Build(NewEvent, NewGreeter, NewMessage) return Event{} }
與其依次初始化每一個組件並將其傳遞給下一個組件,不如使用一個鏈接調用。構建傳遞咱們想要使用的初始化器。在Wire中初始化器被稱爲「提供者」,即提供特定類型的函數。咱們爲事件添加一個零值做爲返回值,以知足編譯器的要求。注意,即便咱們向事件添加值,Wire也會忽略它們。事實上,注入器的目的是提供關於使用哪些提供者來構造一個事件的信息,所以咱們將在文件頂部的build約束中把它從最終的二進制代碼中排除掉:
//+build wireinject # 相似 //+build wireinject // The build tag makes sure the stub is not built in the final build. package main
InitializeEvent是一個「注入器」。如今咱們已經完成了注入器,可使用wire命令行工具了。
# 安裝工具 go get github.com/google/wire/cmd/wire
而後在與上述代碼相同的目錄中,簡單地運行wire。Wire將找到InitializeEvent注入器並生成一個函數,其主體由全部必要的初始化步驟填充。生成文件爲wire_gen.go
// wire_gen.go func InitializeEvent() Event { message := NewMessage() greeter := NewGreeter(message) event := NewEvent(greeter) return event }
它看起來就像咱們上面寫的同樣!如今,這是一個只有三個組件的簡單示例,所以手工編寫初始化器並不太困難。想象一下,對於複雜得多的組件,Wire是多麼有用。當使用Wire時,咱們將提交兩個Wire。去wire_gen。轉到源代碼控制。
爲了演示Wire如何處理更復雜的設置,讓咱們重構Event的初始化器,以返回一個錯誤,而後看看會發生什麼。
func NewEvent(g Greeter) (Event, error) { if g.Grumpy { return Event{}, errors.New("could not create event: event greeter is grumpy") } return Event{Greeter: g}, nil }
咱們會說有時候一個迎賓員可能脾氣暴躁,因此咱們不能建立一個事件。NewGreeter的初始化如今看起來是這樣的:
func NewGreeter(m Message) Greeter { var grumpy bool if time.Now().Unix()%2 == 0 { grumpy = true } return Greeter{Message: m, Grumpy: grumpy} }
咱們已經向Greeter結構中添加了一個grumpy的字段,若是初始化器的調用時間與Unix時代相比是偶數秒,那麼咱們將建立一個暴躁的Greeter,而不是一個友好的Greeter。
func (g Greeter) Greet() Message { if g.Grumpy { return Message("Go away!") } return g.Message }
如今你明白了,一個脾氣暴躁的迎賓員對一件事是多麼地不利。所以NewEvent可能會失敗。咱們的主如今必須考慮到InitializeEvent可能會失敗:
func main() { e, err := InitializeEvent() if err != nil { fmt.Printf("failed to create event: %s\n", err) os.Exit(2) } e.Start() }
咱們還須要更新InitializeEvent,將錯誤類型添加到返回值:
// wire.go func InitializeEvent() (Event, error) { wire.Build(NewEvent, NewGreeter, NewMessage) return Event{}, nil }
再次運行wire生成代碼,以下
// wire_gen.go func InitializeEvent(phrase string) (Event, error) { message := NewMessage(phrase) greeter := NewGreeter(message) event, err := NewEvent(greeter) if err != nil { return Event{}, err } return event, nil }
Wire檢查注入器的參數,看到咱們向參數列表中添加了一個字符串(例如,phrase),一樣看到在全部提供程序中,NewMessage接受一個字符串,所以它將phrase傳遞給NewMessage。