Subtypes Supertypes in go - go

Coming from OOP paradigms and porting a code from an OOP language, I come across a problem now which is solved in OOP via abstraction so I'm wondering how can I approach the following problem in Go which follows composition instead of inheritance.
In this scenario my ValueObjects (DTO, POJO etc.) are composed of other ValueObjects. I'm populating them through web service calls that returns json so basically my functions/method calls are common for all types and subtypes.
My super type EntityVO
type EntityVO struct {
EntityName string
EntityType string
PublicationId string
Version string
}
A subtype 1 composed with EntityVO
type ArticleVO struct {
EntityVO
ContentSize string
Created string
}
subtype 2 composed with EntityVO with it's own unique set of fields
type CollectionVO struct {
EntityVO
ProductId string
Position string
}
I'm calling web services to retrieve data and populate these VOs.
Earlier I had one function to call the web service and populate the data but now I'm duplicating the code for each VO.
type Article struct{}
func (a *Article) RequestList(articleVO *valueObject.ArticleVO) (*valueObject.ArticleVO, error) {
// some code
}
Duplicating the same code but changing the signature.
type Collection struct{}
func (c * Collection) RequestList(collectionVO *valueObject.CollectionVO) (*valueObject.ArticleVO, error) {
// some code - duplicate same as above except method signature
}
and I've several entities and just because my VO's are different I'm forced to duplicate the code and cater to each type of VO I've. In OOP sub types can be passed to a function accepting super types but not in go, so wondering how it should be done so I don't end up duplicated code that's different in signature only?
Any advice for a better approach in this kind of scenario?

This is where golang interfaces can shine.
It's worth noting, however, that it's difficult to write subclass/inheritance code in golang. We'd prefer thinking of it as composition.
type EntityVO interface {
GetName() string
SetName(string) error
GetType() string
...
}
type EntityVOImpl struct {
EntityName string
EntityType string
PublicationId string
Version string
}
func (e EntityVOImpl) GetName() string {
return e.EntityName
}
...
type ArticleVOImpl struct {
EntityVOImpl
ContentSize string
Created string
}
type CollectionVOImpl struct {
EntityVO
ProductId string
Position string
}
// CODE
func (e *Entity) RequestList(entityVO valueObject.EntityVO) (valueObject.EntityVO, error) {
// some code
}
In addition, as long as your interface files are shared, I don't think there should by any problem sending/marshalling/unmarshalling the structs over the wire.

The second parameter of json.Unmarshal is a "universal" interface{} type. I think similar approach is applicable to your case.
Define data type for storing web service properties which may differ for each entity, e.g.
type ServiceDescriptor struct {
URI string
//other field/properties...
}
Function for populating the entity may look like
func RequestList(desc *ServiceDescriptor, v interface{}) error {
//Retrieve json data from web service
//some code
return json.Unmarshal(data, v)
}
You can call the function to populate different type of entities, e.g.
desc := &ServiceDescriptor{}
article := ArticleVO{}
desc.URI = "article.uri"
//...
//You must pass pointer to entity for 2nd param
RequestList(desc, &article)
collection := CollectionVO{}
desc.URI = "collection.uri"
//...
//You must pass pointer to entity for 2nd param
RequestList(desc, &collection)

Related

Golang: How to embed interface with different type parameters?

The code gives me error: DB redeclared.
Is there any idiomatic way to solve it? Or any work-around?
TIA
type a struct {
DB[int64]
DB[string]
}
type b interface {
DB[int64]
DB[string]
}
type DB[T any] interface {
GetList(query string) ([]T, error)
}
You can't embed the same interface, even with different type parameters. Regardless of how it is instantiated, you are trying to promote into the b interface two methods with the same name GetList and different signatures — given by the different instantiations of DB.
The situation is similar, although not technically the same, for embedding into a struct. In structs, the name of the embedded field is the name of the type — DB —, and a struct can't have two non-blank fields with the same name.
About how to solve this issue, it depends what you want to accomplish.
If you want to convey that "a implements DB with either type parameter" you can embed DB[T] and make a itself generic, and restrict a's type parameters:
type a[T int64 | string] struct {
DB[T]
}
// illustrative implementation of the DB[T] interface
// if you embed DB[T] you likely won't use `a` itself as receiver
func (r *a[T]) GetList(query string) ([]T, error) {
// also generic method
}
This is okay because DB's type parameter is constrained to any, and a's type parameter is more restrictive. This allows you to use a in other generic methods, or choose a specific type upon instantiation, but the implementation of GetList has to be parametrized too.
Otherwise if you need a to have separated methods that return int64 or string, you must give it different names.
Finally, you can embed instances of DB into two interfaces with different names, and then embed those into a instead.
type a struct {
DBStr
DBInt
}
type DBStr interface {
DB[string]
}
type DBInt interface {
DB[int64]
}
This way though the top-level selector isn't available because the method names are still the same. The program compiles, but you'll have to explicitly choose which field to call the method on:
myA := a{ /* init the two fields */ }
res, err = myA.DBStr.GetList(query)
// res is type []string
// or
res, err = myA.DBInt.GetList(query)
// res is type []int64

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.

Go struct separation best practices

I am trying to figure out a decent approach toward dealing with multiple uses for a struct. Let me explain the scenario.
I have a struct that represents the Model in gorm. In the current implementation, I have validation bound to this struct so when a request hits the endpoint I would validate against the model's struct. This works fine for most cases. But then there are some instances where I want to have more control over the request and the response.
This is possible by introducing a few additional internal structs that will parse the request and response. And I can decouple the validation from the model into the request specific struct. I am trying to figure out what the best practice is around these patterns. Pretty sure a lot of peeps would have faced a similar situation.
// Transaction holds the transaction details.
type Transaction struct {
Program Program
ProgramID uuid.UUID
Type string
Value float64
Reference string
}
// TransactionRequest for the endpoint.
type TransactionRequest struct {
ProgramKey string `json:"program_key" validator:"required"`
Type string `json:"type" validator:"required,oneof=credit debit"`
Value float64 `json:"value" validator:"required,numeric"`
Reference string `json:"reference" validator:"required"`
}
Update:
I managed to find a balance by introducing additional tags for update requests, I wrote about how I achieved it here
I faced similar problem and for solving that, defined a method for validating and didn't use tags. I had to, because I follow DDD and we should validate in service layer not API layer.
here is a sample of my apporach:
type Station struct {
types.GormCol
CompanyID types.RowID `gorm:"not null;unique" json:"company_id,omitempty"`
CompanyName string `gorm:"not null;unique" json:"company_name,omitempty"`
NodeCode uint64 `json:"node_code,omitempty"`
NodeName string `json:"node_name,omitempty"`
Key string `gorm:"type:text" json:"key,omitempty"`
MachineID string `json:"machine_id,omitempty"`
Detail string `json:"detail,omitempty"`
Error error `sql:"-" json:"user_error,omitempty"`
Extra map[string]interface{} `sql:"-" json:"extra_station,omitempty"`
}
// Validate check the type of
func (p *Station) Validate(act action.Action) error {
fieldError := core.NewFieldError(term.Error_in_companys_form)
switch act {
case action.Save:
if p.CompanyName == "" {
fieldError.Add(term.V_is_required, "Company Name", "company_name")
}
if p.CompanyID == 0 {
fieldError.Add(term.V_is_required, "Company ID", "company_id")
}
}
if fieldError.HasError() {
return fieldError
}
return nil
}
file's address: https://github.com/syronz/sigma-mono/blob/master/model/station.model.go

How to omit some parameters of structure Gin gonic

I have big structure with more than 50 params
type Application struct {
Id int64 `json:"id"`
FullName string `json:"fullName,omitempty"`
ActualAddress string `json:"actualAddress,omitempty"`
.....
}
I use gin-gonic and when I return application I need to omit some params I've created a function which makes empty some params (playLink) and then gin returns me correct json (without unnecessary values). I heard that reflection isn't fast operation so in our case we can use a lot of ugly if-else or switch-cases. Is there any other solutions faster than reflecting and more beautiful than if-elses?
The thing is that structure params have non-empty values, so they wont by omitted by gin. That's why I've created function to make some params empty before return
The thing is, if you only want to zero a few fields, it's more readable to do it without a function, e.g.
app := Application{}
app.FullName, app.ActualAddress = "", ""
If you want to create a function for it, at least use variadic parameter, so it's easier to call it:
func zeroFields(application *Application, fields ...string) {
// ...
}
So then calling it:
zeroFields(&app, "FullName", "ActualAddress")
Yes, this will have to use reflection, so it's slower than it could be, and error prone (mistyped names can only be detected at runtime). If you want to avoid using reflection, pass the address of the fields:
func zeroFields(ps ...*string) {
for _, p := range ps {
*p = ""
}
}
This way you have compile-time guarantee that you type field names correctly, and that they have string type.
Calling it:
zeroFields(&application.FullName, &application.ActualAddress)
Try it on the Go Playground.
If I understand correctly: you want to return some values from your struct but not all of them? Perhaps a nested struct?
type Application struct {
ID struct {
ID int64 `json:"id"`
} `json:"id"`
Person struct {
Fullname string `json:"Fullname"
} `json:"person"
}
That should let you filter out the fields you want to use.

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

Resources