dousuohe5882 2017-05-30 06:04
浏览 97

在golang中如何通过配置文件中的名称获取接口的实例

I want to know how can I inject dependency flexibly in golang just like java spring, Which is if I want to change a instance of an interface all I have to do is just to change some config file.

First I want to find some function like getTypeByName(), so I can just give a struct name like "mypkg.structName" in config file and load that struct by the function, but it seems that there is no such function in golang.

And now the most doable way i can think is to create a loader model to generate a struct register file to load all custom struct.

But I want to know is there have a easier way to do that, all is there some golang-style way to implement this flexibility?

  • 写回答

1条回答

  • dongyizhui0616 2017-05-30 11:52
    关注

    I think your approach is correct, since you can not instantiate a type if it's not defined during compilation. Otherwise, you need to use plugin.

    I'm not sure whether it's easier or not, but if you want the custom structs being organized in different packages, you can use similar approach as database/sql.

    1. Define a factory package which consists of Factory interface and manage known factories. This package should export Register function for registering custom factory, and New function for creating instance of a given type. For example:

      package factory
      
      import (
          "strings"
          "sync"
      )
      
      type Factory interface {
          New(name string) (interface{}, bool)
      }
      
      var (
          mu        sync.RWMutex
          factories = make(map[string]Factory)
      )
      
      func Register(pkgName string, f Factory) {
          mu.Lock()
          defer mu.Unlock()
      
          if f == nil {
              panic("Factory is nil")
          }
          if _, exist := factories[pkgName]; exist {
              panic("Factory already registered")
          }
      
          factories[pkgName] = f
      }
      
      func New(typeName string) (interface{}, bool) {
          items := strings.Split(typeName, ".")
          if len(items) >= 2 {
              mu.RLock()
              defer mu.RUnlock()
              if f, exist := factories[items[0]]; exist {
                  return f.New(items[1])
              }
          }
          return nil, false
      }
      
    2. Create package which consists of your custom struct and implementing the Factory interface. Register the factory during initalization i.e. in init() function, e.g

      package pkga
      
      import "path/to/your/factory"
      
      type thisFactory struct {
      }
      
      type Alpha struct{}
      type Beta struct{}
      type Gamma struct{}
      
      func (f *thisFactory) New(name string) (interface{}, bool) {
          switch name {
          case "Alpha":
              return &Alpha{}, true
          case "Beta":
              return &Beta{}, true
          case "Gamma":
              return &Gamma{}, true
          }
          return nil, false
      }
      
      func init() {
          factory.Register("pkga", &thisFactory{})
      }
      
    3. Repeat step (2) as you need.

    4. Finally, in your main package, you can create an instance as follows:

      package main
      
      import (
          "fmt"
      
          "path/to/your/factory"
          _ "path/to/custom/pkga"
          _ "path/to/custom/pkgb"
          //add more packages ...
      )
      
      func main() {
          a, _ := factory.New("pkga.Alpha")
          b, _ := factory.New("pkgb.Beta")
          c, _ := factory.New("pkga.Gamma")
      
          fmt.Printf("%T %T %T
      ", a, b, c)
      }
      

    Update: a working example is available here.

    评论

报告相同问题?

悬赏问题

  • ¥15 Vue3 大型图片数据拖动排序
  • ¥15 划分vlan后不通了
  • ¥15 GDI处理通道视频时总是带有白色锯齿
  • ¥20 用雷电模拟器安装百达屋apk一直闪退
  • ¥15 算能科技20240506咨询(拒绝大模型回答)
  • ¥15 自适应 AR 模型 参数估计Matlab程序
  • ¥100 角动量包络面如何用MATLAB绘制
  • ¥15 merge函数占用内存过大
  • ¥15 使用EMD去噪处理RML2016数据集时候的原理
  • ¥15 神经网络预测均方误差很小 但是图像上看着差别太大