【Go】GoでDI(Dependency Injection)をやってみた【google/wire】
Go言語を勉強し始めて、DIはどのようにやるのか気になったので、
実際に手を動かして試して行きたいと思います!
自分自身Go触りたてなので、まずは動くことを目的としてやっていきます!
使用したライブラリ
wireを使用しました! Googleが提供しているので安心感がありますね。
tutorial
実装編
最終的なディレクトリ構造
.
├── Dockerfile
├── di
│ ├── wire.go
│ └── wire_gen.go
├── docker-compose.yml
├── go.mod
├── go.sum
├── main.go
├── player
│ └── Player.go
└── shot
└── Layup.go
実装方針
Player構造はLayup構造に依存しているので、外側から依存性を注入することを目指します。
実装
ベースとなるコードの作成
まずはベースとなる2つのstructを定義、またそのstructを生成するfuncをそれぞれ作成します。
命名として、phpの癖でconstructorを含ませました(笑)。wireのドキュメントではprovideという名前が使われていました。
package player
import (
"go_di_sample/shot"
)
type Player struct {
layup shot.Layup
}
func PlayerConstructor(layup shot.Layup) Player {
return Player{layup}
}
func (p *Player)Shot(){
p.layup.Shot()
}
package shot
import "fmt"
type Layup struct {
}
func LayupConstructor() Layup {
return Layup{}
}
func (l *Layup) Shot(){
fmt.Println("layup!!!")
}
DIの準備
プロジェクトフォルダ内の場所に、任意のファイル名でDI準備用のファイルを用意します。
wireはその準備用のファイルを使用して、新たなファイルを作成し、その中で依存性の解決を行なってくれます。
私はdi配下にwire.goを配置しました。
//go:build wireinject
// +build wireinject
package di
import (
"go_di_sample/player"
"go_di_sample/shot"
"github.com/google/wire"
)
type DiTarget struct {
Player player.Player
Layup shot.Layup
}
func InitialDi() (*DiTarget, error){
wire.Build(player.PlayerConstructor, shot.LayupConstructor, wire.Struct(new(DiTarget), "*"))
return nil, nil;
}
ファイル生成
以下のコマンドを実行。
wire gen di/wire.go
生成されたファイル
上記コマンドにより「wire_gen.go」ファイルが生成されました。
// Code generated by Wire. DO NOT EDIT.
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package di
import (
"github.com/google/wire"
"go_di_sample/player"
"go_di_sample/shot"
)
// Injectors from wire.go:
func InitialDi() (*DiTarget, error) {
layup := shot.LayupConstructor()
playerPlayer := player.PlayerConstructor(layup)
diTarget := &DiTarget{
Player: playerPlayer,
Layup: layup,
}
return diTarget, nil
}
// wire.go:
type DiTarget struct {
Player player.Player
Layup shot.Layup
}
var SuperSet = wire.NewSet(player.PlayerConstructor, shot.LayupConstructor, wire.Struct(new(DiTarget), "*"))
wire.goでstruct生成funcをwire.Buildに指定することで、wire_gen.goの以下の部分で
外部からの注入を行なってくれました。
layup := shot.LayupConstructor()
playerPlayer := player.PlayerConstructor(layup)
またwire.go、wire_gen.go共にビルドタグを書いています。
つまりbuild実行時に、オプションを使用なければwire_gen.goがビルド対象となり、
wire.goは本番環境で必要なくなります。つまりwire.goでビルドタグの記述を書かないと、コンパイラーより
DiTarget redeclared in this block
と表示がされます。
main.goで使う
wire_gen.goで生成されたInitialDi funcを使ってみます。
package main
import (
"go_di_sample/di"
)
func main() {
diTarget,_ := di.InitialDi()
diTarget.Player.Shot()
}
コマンドを実行し、正常に動作しました。
go run main.go
layup!!! ←実行結果
Discussion