【GoCNクールGo推奨】依存注入ツールコードジェネレータwire


Golang|wireライブラリ
概要
Wireは、コードを自動的に生成することによって依存注入を完了するコード生成ツールです.
シーンの適用
Wireは注入に依存するコード生成ツールとして,複雑なオブジェクトの作成に非常に適している.大規模なプロジェクトでは、適切な依存注入の枠組みを持つことで、プロジェクトの開発とメンテナンスが便利になります.
Wireコアコンセプト
Wireの最も核心的な2つの概念はInjectorとProviderです.
Provider:コンポーネントを生成する一般的な方法.これらのメソッドは、必要な依存をパラメータとして受信し、コンポーネントを作成して返します.
Injector:最終的に生成するコンストラクション関数を表す関数署名、戻り値はコンストラクションのターゲットを表し、最終的に生成されたコードでは、この関数署名は完全に保持されます.
インストール
    go get github.com/google/wire/cmd/wire

コード生成
コマンドラインは、指定されたディレクトリの下でwireコマンドを実行すればよい.
サンプル学習
https://github.com/google/wire/tree/main/internal/wire/testdata/
メンバーの紹介
func NewSet(...interface{}) ProviderSet
func Build(...interface{}) string
func Bind(iface, to interface{}) Binding
func Struct(structType interface{}, fieldNames ...string) StructProvider
func FieldsOf(structType interface{}, fieldNames ...string) StructFields
func Value(interface{}) ProvidedValue
func InterfaceValue(typ interface{}, x interface{}) ProvidedValue


ベースコード
main.go
package main


type Leaf struct {
    Name string
}

type Branch struct{
    L Leaf
}

type Root struct {
    B Branch
}

func NewLeaf(name string) Leaf {return Leaf{Name:name}}
func NewBranch(l Leaf) Branch {return Branch{L:l}}
func NewRoot(b Branch) Root {return Root{B:b}}

wire.go
// +build wireinject

// The build tag makes sure the stub is not built in the final build.

package main

import (
    "github.com/google/wire"
)

func InitRoot(name string) Root {
    wire.Build(NewLeaf,NewBranch,NewRoot)
    return Root{}
}

wire_gen.go
// Code generated by Wire. DO NOT EDIT.

//go:generate wire
//+build !wireinject

package main

// Injectors from wire.go:

func InitRoot(name string) Root {
 leaf := NewLeaf(name)
 branch := NewBranch(leaf)
 root := NewRoot(branch)
 return root
}

コードの生成はwireに基づいていることがわかります.Buildパラメータの入力と出力タイプによって決まります.
wire.BuildのパラメータはProviderの不定長リストです.
Wireパッケージメンバーの役割
WireのメンバーはそれぞれProviderサービスのためで、それぞれに適用されるシーンがあります.
NewSet 
NewSetの役割は、Providerが多すぎて混乱を招くことを防止するために、ビジネス関連のProviderのセットを置いてProviderSetに組織することです.
wire.goは書くことができます
var NewBranchSet = wire.NewSet(NewLeaf,NewBranch)
func InitRoot(name string) Root {
    wire.Build(NewBranchSet,NewRoot)
    return Root{}
}

新しいSetは、元の構造体が存在するファイルに書くことができ、切り替えとメンテナンスを容易にすることができます.
Bind
Bind関数の役割は,インタフェースタイプをwireの構築プロセスに参加させるためである.Wireの構築はパラメータのタイプに依存してコードを整理するため,インタフェースタイプは天然にサポートされていない.Bind関数は,インタフェースタイプと実装タイプを結合することで,注入依存性を達成する.
type Fooer interface{
    HelloWorld() 
}
type Foo struct{}
func (f Foo)HelloWorld(){}

var bind = wire.Bind(new(Fooer),new(Foo))

サンプルアドレス:https://github.com/google/wire/tree/main/internal/wire/testdata/BindInjectorArgPointer
これによりbindがNewSetまたはBuildに転送され、FooerインタフェースとFooタイプがバインドされます.
ここでは特に注意が必要ですが、*FooがFooerインタフェースを実現している場合は、最後のnew(Foo)をnew(*Foo)に変更する必要があります.
Struct
Struct関数は、構造体のProviderを簡略化するために使用され、構造体のProviderがフィールド割り当てのみの場合に使用できます.
// Leaf        ,           ,          
func NewLeaf(name string) Leaf {return Leaf{Name:name}}

//    
//       
wire.Struct(new(Leaf),"Name")
//      
wire.Struct(new(Leaf),"*")

ここでのNewLeaf関数は、以下のフィールド初期化関数の一部によって置き換えることができる.
Struct関数は、BuildまたはNewSetのパラメータにProviderとして表示されます.
FieldsOf
Fieldsof関数は、wire用に構造体の対応するフィールドをProviderとして使用できます.上のコードに基づいて、私たちは以下の等価をします.
//  Leaf Name   Provider
func NewName(l Leaf) string {return l.Name}

//    
//FieldsOf            
wire.FieldsOf(new(Leaf),"Name")


サンプルアドレス:https://github.com/google/wire/tree/main/internal/wire/testdata/FieldsOfStruct
ここのコードは等価ですが、上のコードと共存できません.理由は後で説明します.
Value
Value関数は、基本タイプの属性バインドの特定の値であり、必要に応じてコードを簡略化します.
func NewLeaf()Leaf{
    return Leaf{
        Name:"leaf",
    }
}

//    
wire.Value(Leaf{Name:"leaf"})

以上の2つの関数はProviderとしても等価であり,BuildまたはNewSetに現れる.
InterfaceValue
InterfaceValueの役割はValue関数と似ていますが、InterfaceValue関数はインタフェースタイプに特定の値をバインドします.
wire.InterfaceValue(new(io.Reader),os.Stdin)

あまり使わないので、ここでは詳しく話しません.
戻り値の特殊な場合
戻り値error
Wireは、オブジェクトを返すと同時にerrorを携帯することをサポートします.errorタイプの戻り値に対してもwireはうまく処理できます.
//main.go
func NewLeaf(name string) (Leaf, error) { return Leaf{Name: name}, nil }

//wire.go
func InitRoot(name string) (Root, error) {
    ...
}

//wire_gen.go
func InitRoot(name string) (Root, error) {
    leaf, err := NewLeaf(name)
    if err != nil {
        return Root{}, err 
    }   
    branch := NewBranch(leaf)
    root := NewRoot(branch)
    return root, nil 
}

サンプルアドレス:https://github.com/google/wire/tree/main/internal/wire/testdata/ReturnError
Providerにerrorの戻り値が表示される場合、Injector関数の戻り値にもerrorの戻り値が持ち込まなければならないことがわかります
クリーンアップ関数CleanUp
クリーンアップは、通常、ファイルオブジェクト、socketオブジェクトが関与するコンストラクション関数に表示されます.エラー後のリソースが閉じられても、オブジェクトが正常に取得された構造関数としても必要です.
クリーンアップ関数は通常、第2の戻り値として機能し、パラメータタイプはfunc()であり、すなわち、パラメータなしで戻り値のない関数オブジェクトである.errorと同様に、Providerのいずれかにクリーンアップ関数がある場合、Injectorの関数署名戻り値にもその関数タイプが含まれている必要があります.
//main.go
func NewLeaf(name string) (Leaf, func()) {
    r := Leaf{Name: name}
    return r, func() { r.Name = "" }
}
func NewBranch(l Leaf) (Branch, func()) { return Branch{L: l}, func() {} }


//wire.go
func InitRoot(name string) (Root, func()) {...}

//wire_gen.go
func InitRoot(name string) (Root, func()) {
    leaf, cleanup := NewLeaf(name)
    branch, cleanup2 := NewBranch(leaf)
    root := NewRoot(branch)
    return root, func() {
        cleanup2()
        cleanup()
    }   
}

サンプルアドレス:https://github.com/google/wire/tree/main/internal/wire/testdata/Cleanup
こうしてcleanupという名前のクリーンアップ関数はInitRootとともに返された.複数のProviderにcleanupがある場合、wireは自動的にcleanupを最後の戻り関数に追加します.
よくある質問
タイプの繰り返し
ベース・タイプ
ベースタイプは構造体を構築する基礎であり、パラメータとして構造体を作成することは非常に一般的であり、パラメータタイプの重複はさらに避けられない.WireはGo言語文法の「type A B」の方法で語類問題を解決する.
//wire.go
type Account string
func InitRoot(name string, account Account) (Root, func()) {...}
   wire.go  "type A B"       wire_gen.go 

サンプルアドレス:https://github.com/google/wire/tree/main/internal/wire/testdata/InjectInput
個人的な観点wireは複雑なオブジェクトの構築に着目しているため,ベースタイプの属性付与は構造体自体のSet操作で行うことを推奨する.
オブジェクトタイプの繰り返し
各Providerは1つのコンポーネントの生成方法であり、2つのProviderが同じクラスのコンポーネントを生成すると、構築中に競合が発生します.ここでは、コンポーネントのタイプの一意性を保証するために特に注意する必要があります.
ループコンストラクション
ループコンストラクションとは、複数のProviderが互いにパラメータと戻り値を提供して閉ループを形成することを意味します.wireが構築のプロセスに閉ループ構築が含まれていることを確認すると、エラーが表示されます.
type Root struct{
    B Branch
}
type Branch struct {
    L Leaf
}
type Leaf struct {
    R Root
}
func NewLeaf(r Root) Leaf {return Leaf{R:r}}
func NewBranch(l Leaf) Branch {return Branch{L:l}}
func NewRoot(b Branch) Root {return Root{B:b}}

...
wire.Build(NewLeaf,NewRranch,NewRoot) //   cycle for XXX
...

サンプルアドレス:https://github.com/google/wire/tree/main/internal/wire/testdata/Cycle
小結
Wireは、Goプログラムを実行せずに特定のファイル(「//+build wireinject」)の解析により、オブジェクトのコンストラクション関数コードを自動的に生成する強力なツールです.
Go言語のエンジニアリングでは、多くのオブジェクトのパッケージレベルの分類に関連しており、wireは複雑なオブジェクトの構築プロセスを完了するのに役立ちます.
もっと知りたいですか?
詳細は以下を参照してください.https://github.com/tidwall/gjson
GOLANG中国コミュニティへようこそ:https://gocn.vip/
『クールGoおすすめ』募集:
Gopherさん、最近、私たちのコミュニティはGoCN毎日新聞のような新しい欄「クールGo推薦」を発売するつもりです.主に毎週1つのライブラリや良いプロジェクトを推薦して、それからこのライブラリの使用方法や長所などを書いて、このようにして本当に大家が新しいライブラリを学ぶことができて、どのように使うかを知ることができます.
大体毎日新聞と似ていますが、応募者が多ければ1人1ヶ月に1回の順番になりますので、ご応募ください.
クリックして原文を読んですぐに申し込みます