文章未來將更新於:
https://kevinyay945.com/golang-project-design/2023/di&constructor/
當你的程式越來越大,你需要依賴的東西就會越來越多
這時候你就會發現前一篇提到的依賴注入是一件很擾人的事情
如果一個struct裡面有三個interface要依賴,而這三個interface也有各自的interface要依賴,那在未來如果正式要運行程式的時候,光是初始化就是一件令人頭痛的事情
因次,又有人專門為了這個問題寫出了一個工具來進行管理
在golang中有不少套專門在管理這種DI的問題的工具,這次我選擇的是Wire
這套工具他很棒的是他做的其實只是幫你產生自動的註冊的程式碼,而你要做的就是把他會用到的struct的constructor全部餵給他,接下來他就會去檢查,你這個constructor需要什麼樣結構的input,他就會自動幫你找到相對應你提供的內容幫你做填入
舉例
以下是我所提供的constructor
interface/rest_api/echo.go
type EchoServer struct {
fileStorer application.FileStorer
}
func NewEchoServer(fileStorer application.FileStorer) ServerInterface {
return &EchoServer{fileStorer: fileStorer}
}
application/file_store.go
type FileStorer interface {
UploadAsset(filename string, i []byte, s string) (file domain.CloudFile, err error)
GetPreviewLink(asset domain.CloudFile) (link string, err error)
}
type FileStore struct {
}
func NewFileStore() FileStorer {
return &FileStore{}
}
di/di.go
type DI struct {
RestAPI api.ServerInterface
}
func NewDI(restAPI api.ServerInterface) *DI {
return &DI{RestAPI: restAPI}
}
而接下來是wire的設定檔
di/di_wire.go
//go:build wireinject
// +build wireinject
// The build tag makes sure the stub is not built in the final build.
package di
import (
"2023_asset_management/application"
api "2023_asset_management/interface/rest_api"
"github.com/google/wire"
)
// InitializeAuthCmd creates an Auth Init Struct. It will error if the Event is staffed with
// a grumpy greeter.
func InitializeDICmd() *DI {
wire.Build(
application.NewFileStore,
api.NewEchoServer,
NewDI,
)
return nil
}
在事前,我們預先寫好了好幾個struct的constructor
func NewEchoServer(fileStorer application.FileStorer) ServerInterface
func NewFileStore() FileStorer
func NewDI(restAPI api.ServerInterface) *DI
而在這些constructor需要的參數,是可以全部都被滿足的
只要照著這個順序,就可以完成依賴注入,所以看到di_wire.go我們就放入了這三個func
緊接著執行
wire
接下來就會順利產生出
// Code generated by Wire. DO NOT EDIT.
//go:generate go run github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package di
import (
"2023_asset_management/application"
"2023_asset_management/interface/rest_api"
)
import (
_ "github.com/google/subcommands"
)
// Injectors from di_wire.go:
// InitializeAuthCmd creates an Auth Init Struct. It will error if the Event is staffed with
// a grumpy greeter.
func InitializeDICmd() *DI {
fileStorer := application.NewFileStore()
serverInterface := api.NewEchoServer(fileStorer)
di := NewDI(serverInterface)
return di
}
此時,你就可以在你需要的地方使用
InitializeDICmd
這個func就可以輕鬆取得需要的DI物件了
另外分享幾個注意事項
在使用wire的時候,如果最後你的output是一個struct,那你在定義的時候要回傳同一個struct的空物件,如果是指標或是interface的話,要回傳nil,這點要特別注意
func NewDI(restAPI api.ServerInterface) *DI {
return &DI{}
}
func InitializeDICmd() *DI {
wire.Build(
application.NewFileStore,
api.NewEchoServer,
NewDI,
)
return nil
}
func NewDI(restAPI api.ServerInterface) DI {
return DI{}
}
func InitializeDICmd() DI {
wire.Build(
application.NewFileStore,
api.NewEchoServer,
NewDI,
)
return DI{}
}
另外,會建議在di的檔案中加上這段內容,來避免在產生程式碼的時候,出現找不到依賴的問題發生
_ "github.com/google/subcommands"
package di
import (
api "2023_asset_management/interface/rest_api"
_ "github.com/google/subcommands"
)
type DI struct {
RestAPI api.ServerInterface
}
func NewDI(restAPI api.ServerInterface) *DI {
return &DI{RestAPI: restAPI}
}