How can I store types in go maps for initialisation at a later stage

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

How can I store types in go maps for initialisation at a later stage

问题

我正在尝试实现一个工厂函数,该函数将返回满足接口X合约的多个结构体之一的实例。

m := make(map[string] ?)
func init () {
    m["a"] = ?
    m["b"] = ?
}

type X interface { 
    y()
}

type A struct {}
func (a *A) y() {}

type B struct {}
func (b *B) y() {}


func factory(name string) X {
    return &m[name]{}
}

上面的代码只是我尝试实现的一个简化示例,我想知道是否有可能实现这个目标,或者是否有其他解决这种需求的Go语言惯用法我没有注意到。

英文:

I'm trying to implement a factory function that will return an instance of one of many structs that fulfil the contract of an interface X.

m := make(map[string] ?)
func init () {
    m["a"] = ?
    m["b"] = ?
}

type X interface { 
    y()
}

type A struct {}
func (a * A) y () {}

type B struct {}
func (b * B) y () {}


function factory(name string) X {
    return &m[name]{}
}

The code above is just a simplified demonstration of what I'm trying to achieve - looking for pointers for whether this is possible, or if there is a different go idiom to solve this kind of requirement that I'm missing.

答案1

得分: 1

你可以使用map[string]X,其中X是接口(可以引用符合X合同的任何对象的值或指针)

或者是否有其他解决这种要求的go习惯用法我没有注意到?

你还可以使用反射(如“实例化新类型”)来实现工厂。

reflect.New(yourtype).Elem().Interface()

你可以在“有没有办法从字符串创建结构体实例?”中看到一个工厂示例。


对于工厂方法(每次返回一个新实例的方法),更快的方法是使用switch(如此示例中所示):

// 根据WidgetType创建一个新的Widget接口,并设置WidgetInfo
func New(wt WidgetType, wi WidgetInfo) Widget_iface {
    switch wt {
    case Widget_A:
        return newWidgetA(wi)
    case Widget_B:
        return newWidgetB(wi)
    }
    return nil
}
英文:

You can use map[string]X, with X the interface (which can reference a value or a pointer of any object respecting X contract)

> or if there is a different go idiom to solve this kind of requirement that I'm missing?

You can also use reflection (as in "Instance new Type") to implement your factory.

reflect.New(yourtype).Elem().Interface()

You can see a factory example in "is there a way to create an instance of a struct from a string?".


The quicker approach for a factory method (returning a new instance each time) is using a switch (like in this example):

// Create a new Widget interface based on WidgetType and set WidgetInfo
func New(wt WidgetType, wi WidgetInfo) Widget_iface {
	switch wt {
	case Widget_A:
		return newWidgetA(wi)
	case Widget_B:
		return newWidgetB(wi)
	}
	return nil
} 

答案2

得分: 1

如果您有一个简单的值类型,那么可以像@VonC所说的那样,只需使用map[string]X并返回示例值的副本。

对于其他任何情况,我建议使用一个创建函数的映射,而不是使用反射。就像image包中的image.RegisterFormat一样。

例如(playground):

package main

import "fmt"

type X interface {
        y()
}

type newXFunc func() X

// 这里只有一个映射变量和两个函数,但是
// 这也可以是一个具有两个方法的类型。

var m = map[string]newXFunc{}

func register(name string, fn newXFunc) {
        m[name] = fn
}

func factory(name string) X {
        return m[name]()
}

func init() {
        // 如果register是公开的,那么这些
        // 调用可以在实现A和B的其他包中。
        register("a", NewA)
        // 对于那些没有/不需要
        // 自己独立的“new”函数的简单事物。
        register("b", func() X { return B{} })
}

type A struct{}

func (a *A) y() {}
func NewA() X   { return &A{} }

type B struct{}

func (b B) y() {}

func main() {
        a1 := factory("a")
        b1 := factory("b")
        fmt.Printf("%T\n", a1)
        fmt.Printf("%T\n", b1)
}
英文:

If you have a simple value type then as @VonC said, you can just use map[string]X and return a copy of the exemplar value.

For anything else, instead of using reflection, I'd just use a map of creation functions. Like what the image package does with
image.RegisterFormat.

E.g. (playground):

package main

import "fmt"

type X interface {
        y()
}

type newXFunc func() X

// Here just a map variable and two functions, but
// this could be a type with two methods instead.

var m = map[string]newXFunc{}

func register(name string, fn newXFunc) {
        m[name] = fn
}

func factory(name string) X {
        return m[name]()
}

func init() {
        // If register is exported than these
        // calls can be in other packages that
        // implement A and B.
        register("a", NewA)
        // For simple things that don't have/need
        // their own stand-alone "new" function.
        register("b", func() X { return B{} })
}

type A struct{}

func (a *A) y() {}
func NewA() X   { return &A{} }

type B struct{}

func (b B) y() {}

func main() {
        a1 := factory("a")
        b1 := factory("b")
        fmt.Printf("%T\n", a1)
        fmt.Printf("%T\n", b1)
}

huangapple
  • 本文由 发表于 2015年5月7日 18:36:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/30098611.html
匿名

发表评论

匿名网友

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

确定