対象読者
- Javaなどの言語を通してDI自体には触れたことがある(仕組みぐらいは知っている)。
- (公私問わず)Golangの実装経験がある。
- 上記の経験がなくとも、興味がある。
お題
DI(Dependency Injection)という概念はJavaなどの言語ではもう当たり前のように実装されている。
ただ、フレームワークが仕組みを提供することが多いため、実は意識せず使っていることもあるかも。
この仕組みは、Golangでアプリケーションを作成する際にも役に立つはず。
が、Golangにおいては、Java等の言語のようにデファクトスタンダードなDIフレームワークが存在しない(いや、言い切ってはいけない。2年くらいのGolang経験上、知らないだけ)。
そんなとき、Googleが、DIフレームワークとまでは言わないまでも”DIツール”という位置づけでwireというものを公開していることを知って、試してみようと思った。
(もとはgo cloudというKitに同梱されていたものが切り出されたものであるとのこと。)
今回は、チュートリアルにある内容をそのままトレースするだけ。
なので、「そんなんなら原本を見るからいいよ」という人は下記を参照されたし。
https://github.com/google/wire/blob/master/_tutorial/README.md
作業環境
# OS
$ cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.1 LTS (Bionic Beaver)"
# 言語
$ go version
go version go1.11.4 linux/amd64
実践
wire導入
導入のくだり、及びgo1.11との組み合わせで生じる事象については前回の記事参照。
wireチュートリアル
■DI対象とする構造
構造体群の定義と依存関係
// --------------------------------------------------------
// [Event]
// +-- [Greeter]
// +-- [Message]
// --------------------------------------------------------
// EventはGreeterを持つ
type Event struct {
Greeter Greeter
}
// GreeterはMessageを持つ
type Greeter struct {
Grumpy bool
Message Message
}
// Message型
type Message string
依存関係の生成関数
// Greeterを受け取ってEventを生成する
func NewEvent(g Greeter) (Event, error) {
if g.Grumpy {
return Event{}, errors.New("could not create event: event greeter is grumpy")
}
return Event{Greeter: g}, nil
}
// Messageを受け取ってGreeterを生成する
func NewGreeter(m Message) Greeter {
var grumpy bool
if time.Now().Unix()%2 == 0 {
grumpy = true
}
return Greeter{Message: m, Grumpy: grumpy}
}
// 文字列を受け取ってMessageを生成する
func NewMessage(phrase string) Message {
return Message(phrase)
}
構造体が持つメソッド
// Eventは開始メソッドを持つ
func (e Event) Start() {
msg := e.Greeter.Greet()
fmt.Println(msg)
}
// Greeterは挨拶メソッドを持つ
func (g Greeter) Greet() Message {
if g.Grumpy {
return Message("Go away!")
}
return g.Message
}
■DI構造を組み立てる関数定義
「wireinject」というビルドタグを付けると、「wire」コマンドによってDIソースが自動生成される。
//+build wireinject
package main
import "github.com/google/wire"
// 依存する構造体群を生成する関数をすべて渡して、最終的にEventを構築
func InitializeEvent(phrase string) (Event, error) {
wire.Build(NewEvent, NewGreeter, NewMessage)
return Event{}, nil
}
■main関数
func main() {
e, err := InitializeEvent("hi there!")
if err != nil {
fmt.Printf("failed to create event: %s\n", err)
os.Exit(2)
}
e.Start()
}
■wireコマンドでDI構造組み立てコードを自動生成
上記「wire.go」が存在するパス上でインストール済みの「wire」コマンドを実行すると「wire_gen.go」が自動生成される。
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func InitializeEvent(phrase string) (Event, error) {
message := NewMessage(phrase)
greeter := NewGreeter(message)
event, err := NewEvent(greeter)
if err != nil {
return Event{}, err
}
return event, nil
}
■実行
$ go run main.go wire_gen.go
hi there!
まとめ
これだけの依存構成では、いまいちありがたみが感じられない。
次の記事では、もう少し実際の業務でありそうな構造で試してみよう。
参考
実は既にいろいろ参考になる記事は出ている。
https://qiita.com/po3rin/items/25ffcfc2fa8381b9d591
https://budougumi0617.github.io/2018/12/14/how-to-use-google-wire/
https://blog.ishkawa.org/2018/12/10/1544371624/
https://developers.freee.co.jp/entry/service-infra-and-wire
https://tech.eviry.com/2018/12/27/di-with-wire-golang/
http://midori5.hatenablog.com/entry/2018/07/28/013026