Reputation: 317
Does go allow you to instantiate a struct from a string? (similar to ruby convert class name in string to actual class in Ruby)
I am trying to separate code from a single file into subdirectories but was getting undefined: PngConverter
when running go build
. It may have been a load order issue however now I'm trying to dynamically instantiate the struct.
var converters = map[string]string{
"png": "PngConverter",
"jpg": "JpegConverter",
"jpeg": "JpegConverter",
}
type Converter interface {
convert(*bytes.Buffer) string
}
func selectConverter(converterName string) {
// dynamically initialize the converter with reflect?
}
func ConvertImageToWords(filename string, image *bytes.Buffer) string {
ext = Ext(filename)
if converterName, y := converters[ext]; y {
converter = selectConverter(converterName)
id = converter.convert(image)
}
return nil
}
converters/png_converter.go
type PngConverter struct {}
func (converter PngConverter) convert(in *bytes.Buffer) string {
// ...
}
converters/jpeg_converter.go
type JpegConverter struct {}
func (converter JpegConverter) convert(in *bytes.Buffer) string {
// ...
}
Upvotes: 2
Views: 709
Reputation: 31109
Go is compiling language it means there is no dynamic evaluation, everything is done after compilation. Interpreting languages like Ruby, Python or Javascript has eval
(and analogs) functions for dynamic programming which is conceptually impossible for compiling languages.
If you need a generic type in Go - use interface with no doubt. Also you are very close to a correct approach: you don't need to evaluate a struct from string instead of it you should declare a map of object of Converter interface
types with all required implementations.
var converters map[string]Converter
type Converter interface {
convert(*bytes.Buffer) string
}
type PngConverter struct{}
type JpgConverter struct{}
func (p *PngConverter) convert(b *bytes.Buffer) (repr string) { return }
func (p *JpgConverter) convert(b *bytes.Buffer) (repr string) { return }
func ConvertImageToWords(
filename string,
image *bytes.Buffer,
converters map[string]Converter) (repr string) {
ext := Ext(filename)
if converter, y := converters[ext]; y {
repr = converter.convert(image)
}
return
}
You haven't been asked but this is wrong design:
Yes, they're in a separate directories...
Package is a namespace where you can have multiple files and you don't need to import each of them to another. Package is a directory with go files containing package <name>
as a first line. But if you separated files with different packages (directories) you need to import it if you need to use it in another package. Java-like class-module design is not correct for Go. Just keep all your converters and the interface in the same package.
Upvotes: 1
Reputation: 6335
No, you can't initialize a struct from String representations.
What you can do is pass the interface in ConvertImageToWords
.
func ConvertImageToWords(conv Converter, image *bytes.Buffer) string
Or have the filename converted to interface Converter
within ConvertImageToWords
.
func GetConverter(ext string) Converter {
switch ext {
case "png":
return PngConverter{}
case "jpg":
...
}
func ConvertImageToWords(filename string, image *bytes.Buffer) string {
ext = Ext(filename)
conv := GetConverter(ext)
id := conv.convert(image)
...
}
Also it's better to have the interface method exported :
type Converter interface {
Convert(*bytes.Buffer) string
}
Upvotes: 0