GoのWireについて理解する

到達していたい状態
- Wireとは何かを人に説明できる
- Wireを利用したコードを書ける
やること
- Wireについて調べる
- Wireのドキュメントを読む、手を動かす

wireとは?
wireはDIツールの1つ。
注入する実装ををメインの処理に書くとかなりのコード数になるのと管理が大変なので、DIツールを呼び出すだけにして実装コストを下げる目的。
注入時にもインスタンス同士の依存関係とかも気にする必要があるので、それをよしなにやってくれるツール
DI を愚直に実装しようとすると依存関係の管理が煩雑になります。つまり、どのロジックがどのモジュールを必要としていて、どのような順番でインスタンスを生成・初期化していけばよいか、というかなり面倒なことを考える必要が出てくるのです。
ここで活躍するのが wire で、各ロジックやモジュールの依存関係を適切に整理・解決したコードを生成します。
wireを使う時にで押さえておくべき概念
1. Provider
初期化関数のこと。
特定の型のインスタンスを提供する関数。
→ いわゆる、New****()
みたいなインスタンスを初期化する関数のことですね。
Providerはwire側でも手動で定義できますし、既存のコードにあるものを使用する形でも問題ないです。
2. Injector
プロバイダーを使って依存関係を解決し、必要なコンポーネントを初期化する関数のこと。
→ Providerを組み立てて、オブジェクトが注入されたインスタンスを返してあげる
インジェクターの役割としては以下が上げられます。
- 依存関係を解決して、最終的なインスタンスを返す
- プロバイダーを呼び出す順序を自動で管理する
- 手動の初期化手順を簡略化する
パッケージ管理ツールのDI版みたいなイメージですかね

DIツールとは?
DIを実現するにあたってサポートしてくれるツールのこと。
「DIコンテナ」といったりもする。
以下の記事を見たらわかりやすいのですが、メインからDIツール(DIコンテナ)を呼び出すだけでDIが実現する感じ

wireのチュートリアルをやって、基本的な使い方を見てみる
こちらのWire Tutorialを見ながら使い方を見てみる
Using Wire to Generate Code
wireを使用して依存性注入の処理を簡単にしてみよう
One downside to dependency injection is the need for so many initialization steps. Let's see how we can use Wire to make the process of initializing our components smoother.
まずmain関数で以下のようにInitializeEvent
を定義
func main() {
e := InitializeEvent()
e.Start()
}
次にwire.go
を作成し、InitializeEvent
関数(Injector)を作成する。
wire.Build()
の中にProviderを渡す。
また、返り値はInjectorで初期化したいインスタンスの初期値を返してあげる。
→ 今回だとEventの情報をInjectorで初期化したいので、返り値はEvent{}
func InitializeEvent() Event {
wire.Build(NewEvent, NewGreeter, NewMessage)
return Event{}
}
wire.go
は自動生成の元ファイルで、ビルドには含めたくないのでファイル先頭に以下を記述
//+build wireinject
wireのインストール
% go install github.com/google/wire/cmd/wire@latest
同じディレクトリにて以下を実行。
WireはInjectorと、Injectorが必要としているProviderも探しにいき、必要な初期化ステップを含んだ関数を自動で作成してくれる。
% wire
以下のようにwire_gen.go
が作成される
// wire_gen.go
func InitializeEvent() Event {
message := NewMessage()
greeter := NewGreeter(message)
event := NewEvent(greeter)
return event
}
wire.go
のInjectorはビルドされないようにしているので、プログラムで実際にビルドされるのはwire_gen.go
となる。(メインで使用するのはwire_gen.go
のInjector)

User Guideの気になるところを見てみる
Providerの書き方
基本的にはイメージしているコンストラクタの書き方と同じです
- 他のパッケージからProviderを呼ぶには、通常のコードと同じようにエクスポートされる状態じゃないといけないので注意が必要。(最初が大文字)
- Provider自体も引数を受け取ることができる
- Providerはエラーを返すことができる
- Providerをグループ化した
Provider Sets
を作成することができるvar SuperSet = wire.NewSet(ProvideFoo, ProvideBar, ProvideBaz)
- Provider Setsをさらにグループ化することができる
var MegaSet = wire.NewSet(SuperSet, pkg.OtherSet)
Injectorの書き方
- Injectorは
wire.Build()
を使用した関数の宣言を行う - Injectorの戻り値は重要ではなく、ダミーの初期化されたコンストラクタでok
- コンパイラのために指定をする必要があって、Wireが自動生成する時には無視される
- Wireは実行時依存がない
- Wireで自動生成されたコードはそのままWireなしでもGoのプログラムとして動く
- 一度Injectorを作成した後は、
go generate
で再生成可能。

Advanced Featuresとして、Binding InterfacesやStruct Providersなどがありますが、今回のスクラップの目的は達成したのでこれで完了とします。