英文:
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
的方法。
- 定义一个工厂包,其中包含
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
}
- 创建一个包,其中包含你的自定义结构并实现
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{})
}
-
根据需要重复步骤(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
.
-
Define a factory package which consists of
Factory
interface and manage known factories. This package should exportRegister
function for registering custom factory, andNew
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 }
-
Create package which consists of your custom struct and implementing the
Factory
interface. Register the factory during initalization i.e. ininit()
function, e.gpackage 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{}) }
-
Repeat step (2) as you need.
-
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论