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 ?
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With