Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Encapsulating struct in Go

Tags:

struct

go

I am new to Go. I have read that encapsulation in Go is on the package level. I have a simple web controller use case. I have a struct which comes in as a JSON object and is Unmarshaled into the struct type.

type User struct{
    Name String `json:"name"`
    //Other Variables
}

Now a json can be unmarshaled into type User Struct by json.Unmarshal([]byte). However, this User struct is available to other packages too. How do I make sure that only methods related to User are accessible by other packages.

One solution I could think of :

type User struct{
    name String
}

type UserJSON struct{
    Name String `json:"name"`
}

func DecodeJSONToUser(rawJSON []byte) (User,error) {
    var userJSON UserJSON
    err := json.Unmarshal(rawJSON,&userJSON)
    //Do error handling
    return User{name:userJSON.Name},nil
}

Is there a GOish way to achieve this ?

like image 250
rhumbaJi Avatar asked Mar 06 '18 08:03

rhumbaJi


People also ask

Does Go support encapsulation?

Golang provides encapsulation at the package level. Go doesn't have any public, private, or protected keyword. The only mechanism to control the visibility is using the capitalized and non-capitalized formats. Capitalized Identifiers are exported.

How encapsulation is achieved in Golang?

Encapsulation is achieved by exported elements(variables, functions, methods, fields, structures) from the packages, it helps to control the visibility of the elements(variables, functions, methods, fields, structures). The elements are visible if the package in which they are defined is available in your program.

Why encapsulation?

Advantages of Encapsulation Encapsulation protects an object from unwanted access by clients. Encapsulation allows access to a level without revealing the complex details below that level. It reduces human errors. Simplifies the maintenance of the application.

What is encapsulation in c#?

Encapsulation is defined as the wrapping up of data under a single unit. It is the mechanism that binds together code and the data it manipulates. In a different way, encapsulation is a protective shield that prevents the data from being accessed by the code outside this shield.


1 Answers

You can use a package local struct with a public field so that this struct will not be visible outside the package. Then you can make this struct satisfy some public interface and you have your perfect decoupling:

package user

import "encoding/json"

type User interface {
    Name() string
}

type user struct {
    Username string `json:"name"`
}

func (u *user) Name() string {
    return "Mr. " + u.Username
}

func ParseUserData(data []byte) (User, error) {
    user := &user{}
    if err := json.Unmarshal(data, user); err != nil {
        return nil, err
    }
    return user, nil
}

And the corresponding test:

package user_test

import (
    "testing"

    "github.com/teris-io/user"
)

func TestParseUserData(t *testing.T) {
    data := []byte("{\"name\": \"Uncle Sam\"}")
    expected := "Mr. Uncle Sam"
    if usr, err := user.ParseUserData(data); err != nil {
        t.Fatal(err.Error())
    } else if usr.Name() != expected {
        t.Fatalf("expected %s, found %s", expected, usr.Name())
    }
}

➜ user git:(master) ✗ go test github.com/teris-io/user

ok github.com/teris-io/user 0.001s

You can also convert your package local object to some public object after unmarshaling.

Note: one of the comments mentions how pity it is that due to name clashes (field user.Name on the struct, and method User.Name on the interface) the interface needs to have a different method name. This is not necessary and the code above has been amended correspondingly: the field on the internal structure can have a different name from that in JSON, the corresponding annotation defines the mapping.

like image 84
Oleg Sklyar Avatar answered Oct 09 '22 21:10

Oleg Sklyar