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

huangapple go评论149阅读模式
英文:

In golang how to get an instance of interface by name in config file

问题

我想知道如何在Go语言中像Java Spring一样灵活地注入依赖。也就是说,如果我想要更改接口的实例,我只需要修改一些配置文件。

首先,我想找到一些类似于**getTypeByName()**的函数,这样我就可以在配置文件中给出一个结构体名称,比如"mypkg.structName",然后通过该函数加载该结构体。但是在Go语言中似乎没有这样的函数。

目前我能想到的最可行的方法是创建一个加载器模块,生成一个结构体注册文件来加载所有自定义结构体。

但我想知道是否有更简单的方法来实现这一点,或者是否有一种符合Go语言风格的方式来实现这种灵活性?

英文:

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

得分: 3

我认为你的方法是正确的,因为如果在编译期间未定义类型,则无法实例化该类型。否则,你需要使用plugin

我不确定是否更容易,但如果你希望将自定义结构组织在不同的包中,可以使用类似于database/sql的方法。

  1. 定义一个工厂包,其中包含Factory接口并管理已知的工厂。该包应该导出Register函数用于注册自定义工厂,以及New函数用于创建给定类型的实例。例如:
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
}
  1. 创建一个包,其中包含你的自定义结构并实现Factory接口。在初始化中注册工厂,例如在init()函数中:
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{})
}
  1. 根据需要重复步骤(2)。

  2. 最后,在你的主包中,你可以按如下方式创建实例:

package main

import (
    "fmt"

    "path/to/your/factory"
    _ "path/to/custom/pkga"
    _ "path/to/custom/pkgb"
    // 添加更多包...
)

func main() {
    a, _ := factory.New("pkga.Alpha")
    b, _ := factory.New("pkgb.Beta")
    c, _ := factory.New("pkga.Gamma")

    fmt.Printf("%T %T %T\n", a, b, c)
}

更新:这里提供了一个可工作的示例。

英文:

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\n", a, b, c)
    }
    

Update: a working example is available here.

huangapple
  • 本文由 发表于 2017年5月30日 14:04:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/44254619.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定