This is an expanded explanation from my answer here.
Sometimes we're faced with the situation where we need to interpret an input, and create <type>
based on it. Rightly so, the service providing that input doesn't (and shouldn't) care about our internal type structure, so we need to figure out how to interpret the input so we're executing the correct actions.
In Python, we can achieve this using the getattr()
method to do some cool meta-programming, where we can use the value of string
to get a matching object;
In Go, we don't have access to such a method, and there is no global registry of types we can readily access. So how do we achieve it?
We could build out own registry of types;
The problem with this is that compile-time errors now become run-time errors, aside from the fact that the code is very implicit and has poor readability.
Consider we have the following interface;
type Fruit interface {
GetVariety() string
}
type Apple struct {
variety string
}
func NewApple(variety string) *Apple {
return &Apple{variety: variety}
}
func (a *Apple) GetVariety() string {
return a.variety
}
type Pear struct {
variety string
}
func NewPear(variety string) *Pear {
return &Pear{variety: variety}
}
func (p *Pear) GetVariety() string {
return p.variety
}
What I want to do is take a message, perhaps something as simple as this;
{
fruit: "Apple",
variety: "Golden Delicious"
}
And be able to construct the correct object (in this case Apple
), which it turns out is very simple by linking the constructor funcs to a map key;
var (
fruits = map[string]func(string) Fruit{
"Apple": func(s string) Fruit { return NewApple(s) },
"Pear": func(s string) Fruit { return NewPear(s) },
}
)
Which we can then call in a few useful ways;
someApple := fruits["Apple"]("Golden Delicious")
fruitVariety := someApple.GetVariety()
fmt.Println(fruitVariety)
// OR
fmt.Println(fruits["Apple"]("Golden Delicious").GetVariety())