1 minute read

Wire

為Google開源 用於處理golang依賴注入的工具
主要處理物件初始化前的依賴關係, 以下用pseudo code舉例

假設我有一個app, 需要依賴於database與另一個相依object才能運行

  • no wire

mydb = DB()
obj = Object()
app = App(myapp,obj)
app.run()

  • wire
app = InitApp()
app.run()

以上為簡單的例子, 看起來可能差沒很多, 但在依賴項很多個的時候, 就可以很大程度讓code更簡潔
wire 使用的方式是 code generation, 簡單說就是直接幫你把 需要 套娃物件的部份 直接合併成一個初始化函數

wire變數輸入上有使用限制, wire一個類型只能有一種輸入, e.g. Name string, 就不有其他string類型,
這邊會使用上是建議直接將需要輸入的變數打包成一個 config物件, 直接輸入config , 不會有類型上的問題

實際範例

物件關係, MainApp 相依DB與OtherDependency
DB與OtherDependency的輸入值會定義在 AppConf中

graph TD
    A[AppConf] --> B[DB]
    A[AppConf] --> C[OtherDependency]
    B --> D[MainApp]
    C --> D

main.go

package main

import "fmt"

type AppConf struct {
	Username string
	Password string
	Url      string
	Id       int
	Content  string
}

func NewAppConf() AppConf {
	return AppConf{Username: "hccuse", Password: "dev123", Url: "test_url", Id: 1, Content: "test wire"}
}

type DB struct {
	username string
	password string
	url      string
}


func NewDB(conf AppConf) DB {
	return DB{conf.Username, conf.Password, conf.Url}
}

type OtherDependency struct {
	ID      int
	Content string
}

func NewOtherDependency(conf AppConf) OtherDependency {
	return OtherDependency{conf.Id, conf.Content}
}

type MainApp struct {
	AppDB         DB
	AppDependency OtherDependency
}

func NewMainApp(db DB, dp OtherDependency) MainApp {
	return MainApp{AppDB: db, AppDependency: dp}
}

func (self MainApp) Echo() {
	fmt.Println(self.AppDB.username)
	fmt.Println(self.AppDB.url)
	fmt.Println(self.AppDependency.ID)
	fmt.Println(self.AppDependency.Content)
}

以上物件, 若不使用wire, 正常調用則是

func main(){
conf := NewAppConf()
db := NewDB(conf)
obj := NewOtherDependency(conf)
app = NewApp(db,obj)
app.Echo()
}

這邊若需使用wire,

會需要定義wire.go, 用途是定義依賴關係

需注意, 最上面 +build wireinject 是必須的
使用wire.NewSet定義 ProviderSet, 輸入參數為相依物件的instance,
最後定義一個初始化函數, 返回為輸始化後的目標物件

//go:build wireinject
// +build wireinject

package main

import "github.com/google/wire"

var ProviderSet = wire.NewSet(
	NewAppConf, NewDB, NewOtherDependency, NewMainApp,
)

func InitApp() MainApp {
	wire.Build(ProviderSet)
	return MainApp{}
}

之後在該目錄下執行

wire

就會輸出一個wire_gen.go 裡面就是wire幫我們打包好的初始化函數

以上範例輸出後 wire_gen.go內容


import (
	"github.com/google/wire"
)

// Injectors from wire.go:

func InitApp() MainApp {
	appConf := NewAppConf()
	db := NewDB(appConf)
	otherDependency := NewOtherDependency(appConf)
	mainApp := NewMainApp(db, otherDependency)
	return mainApp
}

// wire.go:

var ProviderSet = wire.NewSet(
	NewAppConf, NewDB, NewOtherDependency, NewMainApp,
)

可以發現InitApp實際上就是上面無wire版本的部份, wire直接幫你產生出來打包成一個初始化函數 \

之後調用就可以直接


func main(){
    app := InitApp()
    app.Echo()
}

其他補充

產生wire_gen.go 後, 若只go build main.go, 會編譯不到wire產生的code

需將main.go 與 wire_gen.go 一起編譯

go run main.go wire_gen.go
## 直接執行

go build main.go wire_gen.go
## 編譯