使用google/wire進行依賴注入

Wire使用教程

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生成代碼

依賴項注入的一個缺點是須要如此多的初始化步驟。讓咱們看看如何使用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進行更改

  • 爲了演示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。

完整的代碼

相關文章
相關標籤/搜索