iT邦幫忙

2023 iThome 鐵人賽

DAY 9
0
自我挑戰組

2023年度學習分享系列 第 9

DI與Constructor

  • 分享至 

  • xImage
  •  

文章未來將更新於:
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需要的參數,是可以全部都被滿足的

  1. 先用NewFileStore產生一個FileStorer
  2. 再用NewEchoServer產生ServerInterface
  3. 最後用NewDI產生一個DI

只要照著這個順序,就可以完成依賴注入,所以看到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}
}

上一篇
testify規劃方式
下一篇
swagger產生Rest API Handler
系列文
2023年度學習分享30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言