I have recently started working with golang so having difficulties understanding on how to achieve same thing which I am able to do it easily in Java or C#. I am trying to make an object of configmanager
class and when the first time configmanager
class is called, it should initialize all my configs and store it in memory in some struct object. And then I should have access to configmanager
object and should be able to access all those configs from my main function using some getters maybe?
Below is my configmanager
go class. It is very simple for now to make it easier to understand.
package config
import (
"encoding/json"
"fmt"
"io/ioutil"
"github.com/david/internal/utilities"
)
type ClientMetrics struct {
ClientMetrics []ClientMetric `json:"clientMetrics"`
}
type CustomerData struct {
Process []string `json:"Process"`
Mat []string `json:"Mat"`
}
type ClientMetric struct {
ClientID int `json:"clientId"`
CustomerData CustomerData `json:"customerData,omitempty"`
LegCustomer []int `json:"legCustomer"`
OtherIds []int `json:"otherIds,omitempty"`
CatID int `json:"catId,omitempty"`
}
func Init(root string) (err error) {
files, err := utilities.FindFiles(root, "process-data.json")
if err != nil {
return fmt.Errorf("cannot find process-data.json file: %v", err)
}
for _, file := range files {
body, err := ioutil.ReadFile(file)
if err != nil {
return fmt.Errorf("unable to read file: %v", err)
}
var auto ClientMetrics
json.Unmarshal(body, &auto)
}
return nil
}
And here is I use it in my main function - This is just basic code just to demonstrate what I am doing but this isn't production ready code.
package main
import (
"github.com/david/internal/config"
)
func main() {
root := "/home/Configurations"
config.Init(root)
//
}
In my above Init
function, I find process-data.json
file if it is there on the disk and then load it in memory by deserializing it into ClientMetrics
object. Everything works fine as shown above.
Problem Statement
Since I am coming from Java and C# background, so my confusion is how can I make an object of configmanager
class and how should I initialize all my configs during the first time when I call configmanager
and also have access to ClientMetrics
struct using some getters. In Java and C#, I used to have constructor where I initialize all these things and then have some getters to access the config. How should I do the same thing in golang.
My main confusion is do we have constuctors in go and how should I make getters to access struct object in my main function? I am just looking for better design in go and how should I do my above code in golang?
Note:
As of now I only have one config which I am loading in my ClientMetrics
struct but I can have few other configs too and they will be loaded in different struct
during the first time initialization.
CodePudding user response:
I can have let's say 10 different configs (files) and each of those configs can have their own structs since it's a different configs so I need to have separate struct for them
That looks like dynamic JSON struct unmarshalling, which was presented in 2015 by John Asmuth in decoding with mixed structures
You can run the following example here.
type Dog struct {
Name string
Frisbees int
}
type Cat struct {
Name string
Strings int
}
type RawAnimal struct {
Type string
Cat
Dog
}
type Animal RawAnimal
func (a *Animal) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, (*RawAnimal)(a)); err != nil {
return err
}
switch a.Type {
case "cat":
return json.Unmarshal(data, &a.Cat)
case "dog":
return json.Unmarshal(data, &a.Dog)
}
return fmt.Errorf("unknown type %q", a.Type)
}
From there, your ConfigManager will instantiate the right Config structure, depending on the raw JSON read.
CodePudding user response:
I think the trouble is that you're looking at Go from a Java/C# perspective and thus struggling to make sense of the features. If you have the time, then I would suggest that you go thru some Go tutorials or books before you start coding (this one is pretty good: https://www.amazon.com/Programming-Language-Addison-Wesley-Professional-Computing/dp/0134190440)
To answer your question directly, what you need to do is to create a function which returns a pointer to an object of the struct (see here for a simple example: https://gobyexample.com/structs)
Taking ClientMetric as an example:
func NewClientMetric(ClientID int, CustomerData CustomerData, LegCustomer []int, OtherIds []int, CatID int) (*ClientMetric, error) {
//validate your inputs
//in case of errors, create and error message in the variable err and then: return nil, err
//other code which would go into a typical constructor would go here
return &ClientMetric{ClientID,CustomerData, LegCustomer, OtherIds, CatID}, nil
}
In this case, the function NewClientMetric
is the constructor and it returns a pointer/reference to the newly created object. It also returns an error object, which is the same as saying that the constructor throws exceptions. Just as you would need to use try/catch in Java, you would need to check to handle the err variable returned by this function.
You would need to make similar functions for each of your types.
As for getters & setters, generally speaking, that should be avoided in Go. You can access the attributes of a struct directly. A function (like a getter) is only useful if you're going to do something to the attribute before returning it. Something like this:
type Customer struct {
FirstName string
LastName string
}
func (this *Customer) GetFullName() string {
return this.FirstName " " this.LastName
}
and these can then be accessed like this:
var customer *Customer
customer = &Customer{"John","Smith")
fmt.Println(customer.FirstName)
fmt.Println(customer.LastName)
fmt.Println(customer.GetFullName())
Please note that attributes, functions and methods which begin with a capital letter are public, the others are private. If FirstName
was written as firstName
, it would not be accessible outside the package in which it was declared.
Please do note that I'm not checking for errors if the pointer is null/nil, but that is always recommended.