LoginSignup
8
8

More than 5 years have passed since last update.

go1.11+google/wireによるDependency Injection

Posted at

対象読者

  • 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ソースが自動生成される。

[wire.go]
//+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関数

[main.go]
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」が自動生成される。

[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

8
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8