[靈性程式設計]GO的依賴注入 AND 自動生成程式碼

selden發表於2021-09-02

依賴

總結下先有的獲取物件依賴方式

  1. 比較原始的New, 全域性global儲存
  2. 基於反射讀取物件的依賴, 程式啟動時由DI庫例項化(代表作dig等)
  3. 基於反射讀取物件的依賴, 編譯前生成完整構建函式(代表作wire等)

第一種:最方便, 直接快捷, 大量依賴時候, 但是因為是手動的, 容易出現例項順序非預期, 不方便自動測試, mock等。

第二種:因為是啟動時反射獲取依賴的, 需要定義額外的函式給DI系統解析, 例如一個結構的注入必須要要額外的程式碼, 非常麻煩, 不建議使用

// 提供者
err := c.Provide(func(conn *sql.DB) (*UserGateway, *CommentGateway, error) {
  // ...
})
if err != nil {
  // ...
}
// 使用者
err := c.Invoke(func(l *log.Logger) {
  // ...
})
if err != nil {
  // ...
}

第三種, 同樣是基於反射, 所以依然需要一個額外函式(只有配置資訊)提供反射資訊, 生成同名函式, 便捷度基本和手動New一致, wire由 Google 開源

func InitializeNewGormProvider() *Gorm {
   wire.Build(NewGormProvider, InitializeNewConfProvider)

   return nil
}

我的方案

原理和wire一樣, 根據配置資訊生成自動構建函式, 但是不基於反射, 因為反射需要程式是完整的, 編譯後才讀取資訊, 相對慢, 需要每個目錄改完手動執行wire .命令(每個目錄每次花費1秒等)。

先看一個場景, 資料庫服務是依賴配置服務, 從結構體就能看出來, 不需要
func InitializeNewGormProvider() *Gorm{} 函式反射, 未了更加準確(防止注入了不需要的內容)新增一個taginject:""@Bean註解

// @Bean
type Gorm struct {
    conf      *Conf `inject:""`
}

所以,注入其實是可以直接基於原始碼的資訊都能實現的。

我只要實現一個go程式碼解析工具,就能生成和wire工具生成相同的程式碼, 因為go原始碼的關鍵字和結構實在是太簡單了, 沒有多少語法糖, 做一下分詞再按語法規則讀取原始碼資訊, 工具實現比較容易

工具使用php實現( 公司都是mac,php環境mac電腦自帶, 方便使用模版生成go程式碼)
github.com/go-home-admin/home-tool...
重要是php解析很快, 整個專案生成一次都是一秒內

ORM 生成程式碼

編寫工具後, 也可以生成其他輔助程式碼, 例如原始結構, 新增@Orm後, 自動根據欄位資訊生成通用程式碼

// @Orm
type Gorm struct {
    Id       uint32 `json:"id"`
    UserName string `json:"user_name"`
}

邏輯就可以直接使用

    u := &UsersTable{}
    data := u.WhereUserName("test").And(func(table *UsersTable) {
        table.WhereId(1).OrWhereId(2)
    }).Or(func(table *UsersTable) {
        table.WhereId(2).Or(func(table *UsersTable) {
            table.WhereId(1)
        })
    }).Find()

    // select * form users where user_name = ? and (id = ? or id = ?) or (id = ? or (id = ?))
    utils.Dump(data)
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章