go wire 入門

一、介紹

wire 是提供 go 的依賴注入

官方包地址爲 https://github.com/google/wire 我們打開官方 url,安裝方式如下

go install github.com/google/wire/cmd/wire

安裝後,就有 wire 命令可以用了,我試試在命令行工具裏,輸入 wire

➜ wire

輸出:

wire: -: no Go files /Users/zhanghaisheng/study/dataStructure/wirewire: generate failed
wire: generate failed

我們查看 wire 命令支持哪些參數,在命令行工具裏,輸入 wire help

➜ wire help

輸出:

Usage: wire <flags> <subcommand> <subcommand args>

Subcommands:
        check            print any Wire errors found
        commands         list all command names
        diff             output a diff between existing wire_gen.go files and what gen would generate
        flags            describe all known top-level flags
        gen              generate the wire_gen.go file for each package
        help             describe subcommands and their syntax
        show             describe all top-level provider sets


Use "wire flags" for a list of top-level flags

可以看到 wire 支持的命令

二、入門案例:controller,service,model 爲例子。

在一個 web 服務,我們會把路由映射到 controller 的方法上,這裏簡單建立一個 controller。我們在一個學習的目錄下建立下面 4 個空文件

.
├── user_controller.go     //controller文件
├── user_model.go           //model 以及數據庫操作文件
├── user_service.go        //user相關的邏輯 
└── user_test.go           //test測試用例文件

user_model.go 文件爲數據結構 model,我們打開寫入以下內容:

type UserModel struct {
    Name string
    Age  int
}

func NewUserModel() UserModel {
    return UserModel{Name: "hi", Age: 10}
}

user_service.go 文件爲 user 服務類,我們在裏面寫一些操作 user 的邏輯。打開文件,寫入以下內容:

type UserService struct {
    userModel UserModel
}

func NewUserService(user UserModel) UserService {
    return UserService{
        userModel: user,
    }
}

func (s UserService) GetName() string {
    return s.userModel.Name
}

UserService 結構體裏面,有屬性字段 userModel,這個在我們 NewUserService() 的時候,以參數傳進來。

user_controller.go 文件爲 controller 類,我們在這裏處理一些用戶參數,以及調度不同的 service,打開寫入以下內容:

type UserController struct {
    userService UserService
}

func NewUserController(userService UserService) UserController {
    return UserController{
        userService: userService,
    }
}

func (c UserController) GetName() string {
    return c.userService.GetName()
}

三、傳統的初始化對象方式

3.1、 初始化 UserModel

我們在 user_test.go 裏輸入如下代碼:

func TestModel(t *testing.T) {
    md := NewUserModel()
    t.Log(md)
}

我們使用的時候,NewUserModel(),沒有傳參,是最簡單的 new

3.2、 初始化 UserService

我們在 user_test.go 裏輸入如下代碼:

func TestService(t *testing.T) {
    service := NewUserService(NewUserModel())
    t.Log(service.GetName())
}

我們使用 NewUserService(NewUserModel()) 初始化 UserService 對象。

3.3、 初始化 UserController

我們在 user_test.go 裏輸入如下代碼:

func TestController(t *testing.T) {
    c := NewUserController(NewUserService(NewUserModel()))
    t.Log(c.GetName())
}

我們使用 NewUserController(NewUserService(NewUserModel())) 初始化 UserController 對象。

3.4 這種方式有什麼問題?

這幾種方式我們都是以對象注入的方式,初始化對象,對於一個 controler,目前只有一個屬性字段 userService,如果有 4,5 個會怎麼樣,那麼這個初始化就會變的非常長!比如下面這樣

NewUserController(NewUserService(NewUserModel()),NewRedisService(NewUserModel()),NewCacheService(NewUserModel()))

有沒有更好的方案?有,依賴注入,wire 庫。

四、使用 wire 依賴注入

我們新建一個同級目錄 wireinject 如下

.
├── user_controller.go
├── user_model.go
├── user_service.go
├── user_test.go
└── wireinject

然後執行

cd wireinject

在 wireinject 下面,新建一個 wire.go 文件,初始代碼如下:

//go:build wireinject
// +build wireinject

package wireinject

4.1 初始化 UserModel

我們在 wire.go 輸入

func NewUserModel() demo.UserModel {
    panic(
        wire.Build(
            demo.NewUserModel,
        ))
}

然後執行

➜ wire

會發現 wireinject 目錄下,多了一個文件 wire_gen.go

.
├── wire.go
└── wire_gen.go

這個爲 wire 命令生成

4.2、 初始化 UserService

我們在 wire.go 裏輸入如下代碼:

func NewUserService() demo.UserService {
    panic(
        wire.Build(
            demo.NewUserModel,
            demo.NewUserService,
        ))
}

我們使用 NewUserService() 初始化 UserService 對象。return 返回值爲 demo.UserService 對象,在函數里用 panic 包裹 wire.build(....), 其中 wire.build 爲我們要依賴注入的函數,把注入到這個函數里,然後自動的生成對象,這裏參數是 demo.NewUserModel,demo.NewUserService, 爲裏面需要初始化的對象。

然後執行

➜ wire

4.3、 初始化 UserController

我們在 wire.go 裏輸入如下代碼:

func NewUserController() demo.UserController {
    panic(
        wire.Build(
            demo.NewUserModel,
            demo.NewUserService,
            demo.NewUserController,
        ))
}

然後執行

➜ wire

4.4 使用

我們在 wireinject 目錄下新建 wire_test.go 並輸入代碼:

package wireinject

import "testing"

func TestModel(t *testing.T) {
    md := NewUserModel()
    t.Log(md)
}

func TestService(t *testing.T) {
    service := NewUserService()
    t.Log(service.GetName())
}

func TestController(t *testing.T) {
    c := NewUserController()
    t.Log(c.GetName())
}

此時發現我們並不需要輸入參數了!這就是 wire 代碼的依賴注入,它自動幫我們注入參數!

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/0S98jBCMHGqbMShP8Gf5gQ