Control over unexported struct fields - go

So this is my first question in stackoverflow. :)
We have defined a struct in org package like below:
type Employee struct {
FirstName, LastName string
salary int
}
and then in main.go file, we are initializing the struct like below:
func main() {
ross := Employee {
FirstName: "Ross",
LastName: "Geller",
}
fmt.Println(ross)
}
The output will be like below:
{Ross Geller 0}
As salary field is not exported from the Employee struct type, so it's displaying the zero value of int. An end-user will assume that the salary of this employee is 0.
So is there any way to control the unexported fields?
What is the best approach to deal with such a problem in a real-time scenario?
Is this really a problem?

If you're really worried about it, you can override the .String of Employee:
https://play.golang.org/p/PncEOGVP2HP
func (e Employee) String() string {
return fmt.Sprintf("%v", struct{
FirstName string
LastName string
}{e.FirstName, e.LastName})
}
But in reality, are they going to be seeing the output from the console of your program? Most likely this is a non-issue.

Well, If you want to initialize the field you can always write an exported function or method to do that such as
func New(first,last,salary string) Employee{
//...
}
The reason why you can have unexported types is to be able to create something called an opaque type.
You can have methods on your data setters and getters and do complex things without worrying about user braking the internal state of your data.
Imagine you are writing a drawing app and you have a painter Struct which keeps track of cursor and current color and stuff. You really would not want your user to be able to mess with your painter manually. that would break everything.
So the user creates the painter through an initializer and passes it around as a Painter type and using methods such as moveTo,lineTo which updates the state internally.

Related

Mapping one type to another

Let's say I have the following types.
type Contract struct {
Id string `json:"id" gorm:"column:uuid"`
Name string `json:"name" gorm:"column:name"`
Description string `json:"descr" gorm:"column:descr"`
ContractTypeId int `json:"contract_type_id" gorm:"column:contract_type_id"`
}
type ContractModel struct {
Id string `json:"id" gorm:"column:uuid"`
Name string `json:"name" gorm:"column:name"`
Description string `json:"descr" gorm:"column:descr"`
}
I have a SQL query using gorm to scan results into a contract object.
How can I map the values from the contract object into contractModel object?
I tried using the package go-automapper as such:
automapper.Map(contract, ContractModel{})
I want to drop the ContractTypeId.
Can I do this for multiple types in a list?
var contractModels []ContractModel
automapper.Map(contracts, &contractModels)
You can do either:
models := []ContractModel{}
automapper.Map(contracts, &models)
Or call automapper.Map in a loop:
models := make([]ContractModel, len(contracts))
for i := range contracts {
automapper.Map(contracts[i], &models[i])
}
You should be aware that automapper uses reflection behind the scenes and thus is much slower than straight forward non-polymorphic copying like #ThinkGoodly suggests. It's a totally fine solution if performance isn't top priority though.

Workaround solution to avoid getters in Golang interface

Thank you for taking some time to read this question!
I'm new to Golang and I've been developing a simple social media API to practice the language in general. In this project I'm using a struct to represent the Model of a Post published by an user, as follows:
// models/post.go
package models
import "time"
type Post struct {
ID uint64
Title string
Content string
AuthorID uint64
Likes uint64
CreatedAt time.Time
}
func (p *Post) ValidateFields() error {
// validate fields
}
This post can be saved to the database with the help of a method that is inside a repository:
// repositories/posts.go
import (
"database/sql"
"models"
)
type PostsRepository struct {
db *sql.DB
}
func NewPostsRepository(db *sql.DB) *PostsRepository {
return &PostsRepository{db}
}
func (r *PostsRepository) Save(models.Post) (uint64, error) {
// Passing the model as a parameter
}
As you can see, the model is being passed as a parameter and that feels like a problem for two reasons:
I only need three fields to save the post to database (Title,
Content and AuthorID) because everything else will be generated
automatically
If I write an unit test to the Save function it will
rely on the model to work, therefore any problems with the model
would impact the test
So with that in mind I thought about changing the parameter from a model to an interface, but since interfaces only accept method signatures and in that specific case I only need attributes to save data on the database, I assume it would need a few getters such as:
type PostInterface interface {
GetTitle() string
GetContent() string
GetAuthorID() uint64
}
(I know it is not idiomatic to call a getter "GetAttribute", but it's just to give you an idea)
Due to that, I'd have to implement these three methods on my Post Model, which would look like
func (p Post) GetTitle() string {
return p.Title
}
func (p Post) GetContent() string {
return p.Content
}
func (p Post) GetAuthorID() uint64 {
return p.AuthorID
}
That doesn't look so good, but it gets worse when we go to the test.
Like I said, I do not want to use the model in the test so I would have to create a struct to serve as a stub or something that has only the three needed fields for the Save to work (which sounds good). However, I'd have to implement these three methods again so that the struct can be accepted as an interface by the Save function (which sounds bad)
It sounds like a lot of unecessary work, Is there any way to work around this? I'm not sure if I'm missing something conceptual about Go or if there are any changes on my architecture that could address to this issue, but I'm having trouble finding alternatives to this
Thank you!
Using a struct in the save function is perfectly fine and considered idiomatic. If this is just a data container I don't see any issues in testing this functionality. You could however opt for a slimmer version of the struct with just the respective fields you require when storing the content in the database.
For instance:
type Content struct {
Title string
Content string
AuthorID uint64
}
If you still want to decouple with an interface you can create a method on the Post model and return the Content struct, personally I don't see the need for this since it will not improve the testability and only increases complexity by using another layer of abstraction.
In your tests you can just create an instance of the Content struct and pass that to the Save function.

Query with 'has one' association (one-to-one)

I'm playing a bit with Gorm while I'm trying to decide which ORM library fit the most for my needs.
Important to mention that I'm currently working with Sqlite.
Following the guide I created two structs:
type Color struct {
gorm.Model
UserID uint
Name string
}
//User struct define a basic user model
type User struct {
gorm.Model
Username string
Email string
FirstName string
LastName string
Password string
CreationDate time.Time
DOB time.Time
IgnoreMe int `gorm:"-"` // Ignore this field
Color Color `gorm:"foreignkey:ColorRefer"`
ColorRefer uint
}
when I'm creating a DB with
func CreateTables() {
user := dm.User{}
color := dm.Color{}
GormDB.CreateTable(&color)
GormDB.CreateTable(&user)
GormDB.Model(&user).AddForeignKey("ColorRefer", "colors(id)", "CASCADE", "CASCADE")
}
or with:
func CreateTables() {
GormDB.AutoMigrate(&dm.User{},&dm.Color{})
}
Sadly it's not working as I would of expect and create the foreign key automatically, but it's works when I do it manually.
My main problem is when I'm trying to query Users
//QueryByStructExample query users table by the struct non-zero (non-default) fields.
func QueryByStructExample(userStruct dm.User) []dm.User {
var results []dm.User
GormDB.Where(userStruct).Find(&results)
return results
}
I created the following function in a try to query users by email with the color property which is my color struct and I tried to play with a lot with the Model,Related and the Association functions, and nothing seems to work and (I'm avoiding to use join by purpose).
The end result is that it query my User but without the color (only with the ID in my colorRefer)
any suggestion?
Do you mean to preload the Color struct? If yes did you try to query it like that
GormDB.Preload('Color').Where(userStruct).Find(&results)

How to embed struct values via an embedded interface : Composable Structs

This question is best described by an example
http://play.golang.org/p/bQuRr0kV-b
I am trying to make a composable struct. In this example, I want to have a Person type with embedded values from either Female or Male. If I were just dealing with structs, I would embed them like this
type Person Struct{
Female
Male
}
I cannot do this however, both because in the actual project, there are a lot of embedded structs and I would prefer to keep the struct clean and composable. But there is also a naming conflict — in this example, both Male and Female contain the field 'Eyes'. Moving the conflicting value to the Person struct is not a viable solution (as many of the other embedded structs do not contain that particular value).
I was hoping to pass these values via a simple interface. Sample Below: When running this code, I get &{Name: Age:0 Type:male GenderType:0x10434120} where GenderType is the pointer to Male struct(in this case). My Goal is to have a flat structure returned that would look like &{Name: Age:0 Type:male Eyes: ChestSize:0}
package main
import "fmt"
type Person struct {
Name string
Age int
Type string
GenderType
}
type GenderType interface {
TypeName() string
}
type Male struct {
Eyes string
ChestSize int
}
type Female struct {
Eyes string
BustSize int
}
func (m *Male) TypeName() string {
return "male"
}
func (f *Female) TypeName() string {
return "female"
}
func main() {
d := NewHuman(new(Male))
fmt.Printf("%+v", d)
}
func NewHuman(gt GenderType) *Person {
return &Person{
Type: gt.TypeName(),
GenderType: gt,
}
}
I don't think it is possible to do this in a flat structure because it would entail changing the memory structure of the struct type during runtime, which go doesn't allow. While you can access embedded fields using GenderType, it's still allowed because you are saying that this will be a reference to an embedded struct that satisfies the interface rather than changing the structure of the struct itself.
I think the better way to marshal into a flat json using this is to keep your embedded structure, but then make the Person struct a Marshaler
You can add your own custom MarshalJSON() (byte[],error) method and use this to make a flat json output.
If you need specialized unmarshaling, then you can do likewise with an (UnmarshalJSON([]byte) error) method tied to Person.
see https://golang.org/pkg/encoding/json/#Marshaler for further reference
Here is a playground that shows what I mean: https://play.golang.org/p/qOl9WSaI3O

When avoiding global vars (/state), i find myself linking objects backwards to its parent. Am I doing this right? if not explain why? and how-else?

Note: Im just picking the current struct/example to explain the problem.
type MsgBoxFactory struct{
db *dbSql //contains conn-pool and other DB related settings/flags
}
func (f *MsgBoxFactory) NewMsgBox(userId string) {
return MsgBox{userId, f.db} //f.db link is inevitable
}
type MsgBox struct {
ownerId string
db *dbSql
}
func (m *MsgBox) NewMessage(content string) *Message {
return Message{content, *m.dbSql} //m.dbSql link is inevitable
}
type Message struct {
content string
//other fields such as recipents, isRead, created time etc.
db *dbSql
}
func (m *Message) Send(to string) {
message.to = to //just imagine this saves the message to database.
m.db.Save(message)
}
I tend to call this "backward-referencing"[i don't know actual name]... Is this the only way? Previously i was "backward-referencing" entire parent objects. Now i find myself "backward-referencing" objects such as config/dbconn etc...
Is this a good way? what is better?
Oh i have also tried closure to get rid of it atleast from view.
type Message Struct{
content string
Send func(string) error // the problem is `json:"-"` needs to be added. Else the objects are not json compatible
}
func (m *MsgBox) NewMsg(content string) *Message {
msg := &Message{ content }
msg.Send = func(to string) error {
return m.db.Save(msg)
}
}
Basically the code looks almost equally cluttered with unnecessary complexity/code
EDIT: The question is not specific to go. Just posted it because i use go. Any tag suggestion is appreciated to open the question for wider community.
I usually implement a model helper relationship.
Where MsgBox is your model which has all the data specific elements (No DB related elements).
The MsgBoxHelper does all your database related work.
(i.e.
err := MsgBoxHelper.Save(MsgBox)
msgBox, err := MsgBoxHelper.Load(Key)
)
Edit:
The advantage with this method is it decouples your Model from the datastore, which should inturn make it easier should you wish to change your underlying technology (Which doesn't often happen). In practice it's more useful should you start doing things like caching.
If generically you are referencing other structures within your model i.e.
type MsgBox struct {
Colour *MsgBoxColour
...
}
type MsgBoxColor struct {
ID int
...
}
then when you load the Model in your MsgBoxHelper you call the MsgBoxColourHelper with the ID you stored in the MsgBoxColour table, this then returns your MsgBoxColour that you then associate with the returning MsgBox.

Resources