I am working with lot of config files. I need to read all those individual config file in their own struct and then make one giant Config
struct which holds all other individual config struct in it.
Let's suppose if I am working with 3 config files.
ClientConfig
deals with one config file.DataMapConfig
deals with second config file.ProcessDataConfig
deals with third config file.
I created separate class for each of those individual config file and have separate Readxxxxx
method in them to read their own individual config and return struct back. Below is my config.go
file which is called via Init
method from main
function after passing path
and logger
.
config.go
package config
import (
"encoding/json"
"fmt"
"io/ioutil"
"github.com/david/internal/utilities"
)
type Config struct {
ClientMapConfigs ClientConfig
DataMapConfigs DataMapConfig
ProcessDataConfigs ProcessDataConfig
}
func Init(path string, logger log.Logger) (*Config, error) {
var err error
clientConfig, err := ReadClientMapConfig(path, logger)
dataMapConfig, err := ReadDataMapConfig(path, logger)
processDataConfig, err := ReadProcessDataConfig(path, logger)
if err != nil {
return nil, err
}
return &Config{
ClientMapConfigs: *clientConfig,
DataMapConfigs: *dataMapConfig,
ProcessDataConfigs: *processDataConfig,
}, nil
}
clientconfig.go
package config
import (
"encoding/json"
"fmt"
"io/ioutil"
"github.com/david/internal/utilities"
)
type ClientConfig struct {
.....
.....
}
const (
ClientConfigFile = "clientConfigMap.json"
)
func ReadClientMapConfig(path string, logger log.Logger) (*ClientConfig, error) {
files, err := utilities.FindFiles(path, ClientConfigFile)
// read all the files
// do some validation on all those files
// deserialize them into ClientConfig struct
// return clientconfig object back
}
datamapconfig.go
Similar style I have for datamapconfig
too. Exactly replica of clientconfig.go
file but operating on different config file name and will return DataMapConfig
struct back.
processdataConfig.go
Same thing as clientconfig.go
file. Only difference is it will operate on different config file and return ProcessDataConfig
struct back.
Problem Statement
I am looking for ideas where this above design can be improved? Is there any better way to do this in golang
? Can we use interface or anything else which can improve the above design?
If I have let's say 10 different files instead of 3, then do I need to keep doing above same thing for remaining 7 files? If yes, then the code will look ugly. Any suggestions or ideas will greatly help me.
Note: I can do validations after unmarshaling.
2 Answers 2
If I understand your problem statement correctly, you have a number of JSON files and you want to do lookup/parsing on each file.
Keep in mind that Go is not an "object-oriented" language, and does not have classes. It is a slight conceptual shift to think about structs as data, rather than a way to organize code.
I would write a helper function that takes as arguments the file name and a struct to serialize into. A sketch of the implementation is given below.
// ReadConfig reads the specified file and then parses the
// file as JSON into the given config structure.
func ReadConfig(filePath string, config any) error {
files, err := utilities.FindFiles(filePath)
if err != nil {
return err
}
if len(files) == 0 {
return errors.New("no files were found")
}
fileContent, err := os.ReadFile(files[0])
if err != nil {
return err
}
return json.Unmarshal(fileContent, config)
}
// Then you would call the helper function as follows
func Init(path string) (Config, error) {
// Parse client map config.
var clientConfig ClientConfig
if err := ReadConfig(path+"/clientConfigMap.json", &clientConfig); err != nil {
return Config{}, err
}
if err := ValidateClientMapConfig(clientConfig); err != nil {
return Config{}, err
}
// Parse data map config.
var dataMapConfig DataMapConfig
if err := ReadConfig(path+"/dataConfigMap.json", &dataMapConfig); err != nil {
return Config{}, err
}
if err := ValidateDataMapConfig(dataMapConfig); err != nil {
return Config{}, err
}
// and so on for other config types
}
The key idea is that json.Unmarshal
already takes a struct of any type, so we make our helper function also accept an any
type, and just pass the value straight to json.Unmarshal
.
I don't think interfaces are a good fit here, as each parsing function has a different type. Interfaces are useful when you have multiple methods with the same name and types, and you need to pick between them at runtime.
I think that you are on the right track when you think of using an interface
. One way to look at this is to make ReadConfig an interface with methods ReadConfig(); etc...
-
2\$\begingroup\$ yeah can you provide some examples on how can I make my current code better using interfaces and all? \$\endgroup\$AndyP– AndyP2022年02月08日 01:07:20 +00:00Commented Feb 8, 2022 at 1:07