AVt天堂网 手机版,亚洲va久久久噜噜噜久久4399,天天综合亚洲色在线精品,亚洲一级Av无码毛片久久精品

當前位置:首頁 > 科技  > 軟件

使用 Google Wire 在 Go 中進行依賴注入

來源: 責編: 時間:2024-05-07 09:09:45 130觀看
導讀關注點分離、松耦合系統和依賴反轉原則等概念在軟件工程中是眾所周知的,并且在創建良好的計算機程序過程中至關重要。在本文中,我們將討論一個同時應用了這三個原則的技術,稱為依賴注入。我們將盡可能地實踐,更加重點地討

關注點分離、松耦合系統和依賴反轉原則等概念在軟件工程中是眾所周知的,并且在創建良好的計算機程序過程中至關重要。在本文中,我們將討論一個同時應用了這三個原則的技術,稱為依賴注入。我們將盡可能地實踐,更加重點地討論如何在 Go 應用程序中實現依賴注入。但在進一步討論之前,讓我們重新審視一下,究竟什么是依賴注入?WBN28資訊網——每日最新資訊28at.com

WBN28資訊網——每日最新資訊28at.com

如前所述,依賴注入是一種技術,其關注點在于確保想要使用特定服務的對象或函數不必知道如何構造這樣的服務,從而將構造對象和使用對象的關注點分離,導致松耦合的程序。接收對象或函數由外部注入器提供其依賴項,而它并不知道這些依賴項。依賴注入還鼓勵使用依賴反轉原則,即接收對象只需要聲明其使用的服務的接口,而不需要它們的具體實現。WBN28資訊網——每日最新資訊28at.com

Robert van Gen 在他的一篇文章中指出,雖然依賴注入在小規模上運行良好,但對于具有復雜依賴關系圖的更大型應用程序,它將導致大量的初始化代碼塊。這就是 Wire 等工具非常有用的地方。Wire 是 Go 中用于依賴注入的代碼生成器。是的,你猜對了,Wire 將為我們生成必要的初始化代碼。我們只需要定義提供程序和注入器。提供程序是普通的 Go 函數,根據它們的依賴項提供值,而注入器是按依賴順序調用提供程序的函數。為了更好地說明這一點,將呈現一個示例。WBN28資訊網——每日最新資訊28at.com

設置游樂場

假設我們正在開發一個 HTTP 服務器,為用戶注冊提供端點。盡管只有一個端點,但它是使用通常出現在更復雜應用程序中的三層設計:存儲庫、用例和控制器。出于不那么重要的原因,讓我們假設它具有以下目錄結構,WBN28資訊網——每日最新資訊28at.com

.├── go.mod├── go.sum├── internal│   ├── domain│   │   ├── model│   │   │   └── user.go│   │   └── repository│   │       └── user.go│   ├── handler│   │   └── handler.go│   ├── interface│   │   └── datastore│   │       └── user.go│   └── usecase│       ├── request│       │   └── user.go│       ├── user│       │   └── user.go│       └── user.go└── main.go

現在,讓我們在 internal/interface/datastore/user.go 中定義我們的第一個提供程序。在以下代碼片段中,New 是一個提供者函數,它以 *sql.DB 為依賴項,并返回 Repository 的具體實現。WBN28資訊網——每日最新資訊28at.com

// internal/interface/datastore/user.gopackage datastoreimport (    "context"    "database/sql"    "inject/internal/domain/model")type Repository struct {    db *sql.DB}func New(db *sql.DB) *Repository {    return &Repository{db: db}}func (r Repository) Create(ctx context.Context, user model.User) error {    // TODO: implement me    return nil}

這個 Repository 的具體實現將通過抽象或接口由 UseCase 層使用。換句話說,我們的 UseCase 層的提供者函數依賴于接口,而不是 Repository 的具體實現。從技術上講,這個接口應該由消費層擁有,但是 — 我個人認為 — 這并不意味著它們兩者必須位于同一個包中。在我們的示例中,Usecase 的提供者和 Repository 的接口分別在 internal/usecase/user/user.go 和 internal/domain/repository/user.go 中定義。WBN28資訊網——每日最新資訊28at.com

// internal/usecase/user/user.gopackage userimport (    "context"    "inject/internal/domain/repository"    "inject/internal/usecase/request")type Usecase struct {    repository repository.Repository}func New(repository repository.Repository) *Usecase {    return &Usecase{repository: repository}}func (u Usecase) Create(ctx context.Context, req request.CreateUserRequest) error {    // TODO: implement me    return nil}

就像之前 Repository 的提供者一樣,這里我們 UseCase 的提供者也返回一個具體的實現。WBN28資訊網——每日最新資訊28at.com

// internal/domain/repository/user.gopackage repositoryimport (    "context"    "inject/internal/domain/model")type Repository interface {    Create(ctx context.Context, user model.User) error}

最后,具體的 UseCase 實現將被 Controller 使用,同樣通過抽象或接口。Controller 的提供程序和 UseCase 的接口在 internal/handler/handler.go 和 internal/usecase/user.go 中定義,如下所示:WBN28資訊網——每日最新資訊28at.com

// internal/interface/datastore/user.gopackage handlerimport (    "inject/internal/usecase"    "net/http")type Handler struct {    usecase usecase.Usecase}func New(usecase usecase.Usecase) *Handler {    return &Handler{usecase: usecase}}func (h Handler) Create() http.HandlerFunc {    return func(w http.ResponseWriter, r *http.Request) {        // TODO: implement me        w.WriteHeader(http.StatusOK)    }}
// internal/usecase/user.gopackage usecaseimport (    "context"    "inject/internal/usecase/request")type Usecase interface {    Create(ctx context.Context, req request.CreateUserRequest) error}

現在,所有必要的提供者都完成了,我們可以在我們的 main.go 中手動執行依賴注入,就像這樣:WBN28資訊網——每日最新資訊28at.com

// main.gopackage mainimport (    "database/sql"    "log"    "net/http"    "inject/internal/handler"    "inject/internal/interface/datastore"    "inject/internal/usecase/user"    _ "github.com/go-sql-driver/mysql")func main() {    db, err := sql.Open("mysql", "dataSourceName")    if err != nil {        log.Fatalf("sql.Open: %v", err)    }    repository := datastore.New(db)    usecase := user.New(repository)    handler := handler.New(usecase)    mux := http.NewServeMux()    mux.HandleFunc("POST /users", handler.Create())    log.Fatalf("http.ListenAndServe: %v", http.ListenAndServe(":8000", mux))}

接下來,如何使用 Wire 生成像上面那樣的初始化代碼呢?WBN28資訊網——每日最新資訊28at.com

使用 Wire

通過 Wire,我們打算使我們最終的 main.go 看起來更簡化,像這樣:WBN28資訊網——每日最新資訊28at.com

// main.gopackage mainimport (    "log"    "net/http")func main() {    handler, err := InitializeHandler()    if err != nil {        log.Fatal(err)    }    log.Fatal(http.ListenAndServe(":8000", handler))}

我們可以開始創建一個文件,通常命名為 wire.go。它可以在一個單獨的包中定義,但在這個示例中,我們將其定義在項目的根目錄。但在繼續創建 wire.go 之前,最好重構我們之前的一些代碼,特別是創建數據庫連接實例和注冊 API 路由的部分。以下新提供者將起到這個目的,WBN28資訊網——每日最新資訊28at.com

// pkg/mysql/mysql.gopackage mysqlimport (    "database/sql"    _ "github.com/go-sql-driver/mysql")func New() (*sql.DB, error) {    db, err := sql.Open("mysql", "dataSourceName")    if err != nil {        return nil, err    }    return db, nil}
// internal/handler/route.gopackage handlerimport "net/http"func Register(handler *Handler) *http.ServeMux {    mux := http.NewServeMux()    mux.HandleFunc("POST /users", handler.Create())    re

提供程序函數 Register 上面接受 Handler 的具體實現。當然,也可以使用抽象或接口。但我們將其保留,就像我們讓 Repository 的提供程序函數接受類型 *sql.DB 的具體實現一樣。這不違反我們之前提到的依賴反轉原則。實際上,這可能是一個很好的例子,如果沒有立即需要的情況,我們不必在我們的代碼中創建抽象。WBN28資訊網——每日最新資訊28at.com

好的,現在讓我們回到我們的 wire.go。根據我們簡化的 main.go 文件,你可能已經意識到 InitializeHandler 函數可能是由 Wire 生成的 — 是的,你說對了!要正確生成這樣的函數,我們可以將我們的 wire.go 編寫如下:WBN28資訊網——每日最新資訊28at.com

//go:build wireinject// +build wireinjectpackage mainimport (    "net/http"      "inject/internal/domain/repository"    "inject/internal/handler"    "inject/internal/interface/datastore"    "inject/internal/usecase"    "inject/internal/usecase/user"    "inject/pkg/mysql"    "github.com/google/wire")func InitializeHandler() (*http.ServeMux, error) {    wire.Build(        mysql.New,        datastore.New,        wire.Bind(new(repository.Repository), new(*datastore.Repository)),        user.New,        wire.Bind(new(usecase.Usecase), new(*user.Usecase)),        handler.New,        handler.Register,    )    return &http.ServeMux{}, nil}

基本上,在 wire.go 中,我們告訴 Wire 關于初始化器函數 InitializeHandler 的模板。它返回 *http.ServeMux 和 error。請注意,(&http.ServeMux{}, nil) 的返回值僅用于滿足編譯器。為了正確返回所需的值,我們在 Build 函數中聲明了所有必要的提供程序:mysql.New、datastore.New、user.New、handler.New 和 handler.Register。WBN28資訊網——每日最新資訊28at.com

雖然 Wire 足夠聰明,可以識別依賴圖,但仍然需要明確告訴它某個具體實現滿足某個接口。記住,datastore.New 和 user.New 返回類型為 *datastore.Repository 和 *user.Usecase 的具體實現,滿足 repository.Repository 和 usecase.Usecase 接口的情況。這兩種情況的必要顯式聲明通過 Bind 函數實現。WBN28資訊網——每日最新資訊28at.com

請注意,我們需要將 wire.go 從最終的二進制文件中排除。這通過在 wire.go 文件頂部添加一個構建約束來完成。WBN28資訊網——每日最新資訊28at.com

接下來,我們可以在應用程序的根目錄中調用 wire 命令,WBN28資訊網——每日最新資訊28at.com

wire

如果之前沒有安裝 Wire,請先運行以下命令,WBN28資訊網——每日最新資訊28at.com

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

這個 wire 命令將生成一個名為 wire_gen.go 的文件,其內容是 InitializeHandler 函數的生成代碼,如下所示:WBN28資訊網——每日最新資訊28at.com

// Code generated by Wire. DO NOT EDIT.//go:generate go run -mod=mod github.com/google/wire/cmd/wire//go:build !wireinject// +build !wireinjectpackage wireimport (    "inject/internal/handler"    "inject/internal/interface/datastore"    "inject/internal/usecase/user"    "inject/pkg/mysql"    "net/http")// Injectors from wire.go://go:generate wirefunc InitializeHandler() (*http.ServeMux, error) {    db, err := mysql.New()    if err != nil {        return nil, err    }    repository := datastore.New(db)    usecase := user.New(repository)    handlerHandler := handler.New(usecase)    serveMux := handler.Register(handlerHandler)    return serveMux, nil}

生成的初始化器代碼看起來與我們之前在 main.go 的第一個版本中編寫的代碼非常相似。WBN28資訊網——每日最新資訊28at.com

修改依賴項

假設我們想要修改我們的 mysql.New 提供者,以接受一個配置結構,因為我們不想直接在其中硬編碼數據源名稱 — 這通常被認為是不好的做法。為了實現這一點,我們創建一個特殊的目錄來存儲應用程序配置文件和一個新的提供者,該提供者讀取文件并返回一個配置結構。我們最終的目錄結構將如下所示:WBN28資訊網——每日最新資訊28at.com

.├── config│   ├── config.go│   └── file│       └── config.json├── go.mod├── go.sum├── internal│   ├── domain│   │   ├── model│   │   │   └── user.go│   │   └── repository│   │       └── user.go│   ├── handler│   │   ├── handler.go│   │   └── route.go│   ├── interface│   │   └── datastore│   │       └── user.go│   └── usecase│       ├── request│       │   └── user.go│       ├── user│       │   └── user.go│       └── user.go├── main.go├── pkg│   └── mysql│       └── mysql.go├── wire_gen.go└── wire.go

在 config/config.go 中,我們定義了 Config 結構以及它的提供者,WBN28資訊網——每日最新資訊28at.com

package configtype Config struct {    DatabaseDSN string    AppPort     string}func Load() (Config, error) {    // TODO: implement me    return Config{}, nil}

接下來,我們只需要將這個新提供者添加到我們的 wire.go 文件中。是的,你說得對,將其作為 Build 函數的一部分插入即可,WBN28資訊網——每日最新資訊28at.com

//go:build wireinject// +build wireinjectpackage wireimport (    "net/http"    "inject/config"    "inject/internal/domain/repository"    "inject/internal/handler"    "inject/internal/interface/datastore"    "inject/internal/usecase"    "inject/internal/usecase/user"    "inject/pkg/mysql"    "github.com/google/wire")func InitializeHandler() (*http.ServeMux, error) {    wire.Build(        config.Load,        mysql.New,        datastore.New,        wire.Bind(new(repository.Repository), new(*datastore.Repository)),        user.New,        wire.Bind(new(usecase.Usecase), new(*user.Usecase)),        handler.New,        handler.Register,    )    return &http.ServeMux{}, nil}

再次運行 wire 命令 — 或者這次我們也可以運行 go generate 命令 — 將告訴 Wire 重新生成初始化代碼,結果如下:WBN28資訊網——每日最新資訊28at.com

// Code generated by Wire. DO NOT EDIT.//go:generate go run -mod=mod github.com/google/wire/cmd/wire//go:build !wireinject// +build !wireinjectpackage wireimport (    "inject/config"    "inject/internal/handler"    "inject/internal/interface/datastore"    "inject/internal/usecase/user"    "inject/pkg/mysql"    "net/http")// Injectors from wire.go:func InitializeHandler() (*http.ServeMux, error) {    configConfig, err := config.Load()    if err != nil {        return nil, err    }    db, err := mysql.New(configConfig)    if err != nil {        return nil, err    }    repository := datastore.New(db)    usecase := user.New(repository)    handlerHandler := handler.New(usecase)    serveMux := handler.Register(handlerHandler)    return serveMux, nil}

很簡單,對吧?WBN28資訊網——每日最新資訊28at.com

最后的話

我們已經介紹了使用 Wire 的簡單示例,演示了它如何幫助我們構建具有依賴注入的初始化代碼。但這并不是 Wire 的全部故事。實際上,它仍然有一些其他有用的功能尚未在這里討論。WBN28資訊網——每日最新資訊28at.com

本文鏈接:http://www.tebozhan.com/showinfo-26-86991-0.html使用 Google Wire 在 Go 中進行依賴注入

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: Java中五個鮮為人知的Collections特性

下一篇: Python打包,setuptools打包六步法

標簽:
  • 熱門焦點
Top