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

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

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

Marshal struct field to JSON without field name

I need to marshal into this JSON format:
{"messageProtocolHandshake":[{"handshakeType":"announceMax"},{"version":[{"major":1},{"minor":0}]}]}
Problem is matching the handshakeType. My struct is
type MessageProtocolHandshake struct {
HandshakeType HandshakeType `json:"handshakeType"`
Version []Version `json:"version"`
}
type HandshakeType struct {
HandshakeType string
}
Marshaling can be done using slice of interface:
func (h MessageProtocolHandshake) MarshalJSON() ([]byte, error) {
res := make([]interface{}, 3)
res[0] = struct {
HandshakeType string `json:"handshakeType"`
}{h.HandshakeType.HandshakeType}
res[1] = struct {
Version []Version `json:"version"`
}{h.Version}
return json.Marshal(res)
}
Using a simple marshaler/unmarshaler takes away the surrounding curly brackets from the handshakeType, so that doesn't work:
{"messageProtocolHandshake":[{"handshakeType":"announceMax","version":[{"major":1,"minor":0}],"formats":[{"format":"JSON-UTF8"}]}]}
Seems as if Go applies some heuristic in that case on the retuned byte array (undocumented?).
Is there a more elegant way of omitting the structs outer field name?
--
UPDATE To summarise the answers: key is to think about different structs for marshalling and unmarshalling if nothing else works, potentially a using a 3rd presentation for working internally with the data.
When custom (Un)Marshalers come into play remember that promoted fields inherit their methods and hence influence parent structs.
The JSON that you specified has a different model from that of your struct.
There are a few approaches to aligning these: Change the specification of the JSON data to match your structs, change the structs to match the specification of the JSON, or create a new struct that is only used for marshaling.
I omit the last example, because it's very similar to the second method.
Changing the specification of the JSON
The following model stays the same:
type MessageProtocolHandshake struct {
HandshakeType HandshakeType `json:"handshakeType"`
Version []Version `json:"version"`
}
type HandshakeType struct {
HandshakeType string
}
The JSON for this would be:
{"handshakeType":{"HandshakeType":""},"version":[]}
You did not specify the Version type so I don't know how one would change the JSON for that.
Changing the structs
The following JSON stays the same:
{"messageProtocolHandshake":[{"handshakeType":"announceMax"},{"version":[{"major":1},{"minor":0}]}]}
The structs for this would be:
type Model struct {
MessageProtocolHandshake []interface{} `json:"messageProtocolHandshake"`
}
type HandshakeType struct {
HandshakeType string `json:"handshakeType"`
}
type Versions struct {
Version []Version `json:"version"`
}
type Version struct {
Major *int `json:"major,omitempty"`
Minor *int `json:"minor,omitempty"`
}
Unmarshaling would not be trivial.
https://play.golang.org/p/89WUhcMFM0B
As is obvious from the results, the models you are using are not good. If there's a way to change all of this, I would recommend starting from scratch, using the data that is necessary and creating the JSON specification from the structs.
I recommend reading up on JSON: https://www.json.org/json-en.html
Also, I recommend this introduction to Go and JSON: https://blog.golang.org/json

How to modify a field in a struct of an unknown type?

I have multiple structs that have one common field; let's call it common here
type Struct1 struct {
foo string
bar string
common string
}
type Struct2 struct {
baz int
qux string
common string
}
I want to create a function that takes an Interface as input and nullifies common. The available struct types won't be known at compile time, so I can't create a separate function for each type, and I can't use a switch statement.
P.S: In my use-case, I want to nullify common because it holds the creation time of each struct, and I want to track the history of the struct, so I will know if it changes. Having the creation time inside the struct will mess this up because the creation time will be different every time a new struct is generated even though its actual data may be the same.
Define a struct with the common fields and make it implement an interface which says that it is able to nullify the common fields. Then embed this struct into your other struct types that should be able to nullify the fields.
// CommonNullifier is able to nullify its common field(s)
type CommonNullifier interface {
NullifyCommon()
}
// StructCommon contains the common struct fields
type StructCommon struct {
Common string
}
func (sc *StructCommon) NullifyCommon() {
sc.Common = ""
}
// Struct1 embeds common fields, thus implements CommonNullifier
type Struct1 struct {
StructCommon
Foo string
}
// Struct2 also embeds common fields, thus also implements CommonNullifier
type Struct2 struct {
StructCommon
Bar string
}
// NullifyCommon nullfies the 'common' fields in the argument
func NullifyCommon(s CommonNullifier) {
s.NullifyCommon()
}
(See it on the Go Playground)
You could also use reflection, but using an interface is generally more readable.

Subtypes Supertypes in 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)

Private embedded struct when importing a struct from another package

I have a project which relies on a struct imported from another package, which I will call TheirEntity.
In the example below, I (ahem) embed TheirEntity in MyEntity, which is an extension of TheirEntity, with added functionality.
However, I don't want to export TheirEntity in the MyEntity structure, as I would rather the consumer not access TheirEntity directly.
I know that Go embedding is not the same as inheritance in classical OOP, so maybe this is not the correct approach, but is it possible to specify embedded structs as "private", even if they are imported from another package? How might one achieve the same thing in a more idiomatic fashion?
// TheirEntity contains functionality I would like to use...
type TheirEntity struct {
name string
}
func (t TheirEntity) PrintName() {
fmt.Println(t.name)
}
func NewTheirEntity(name string) *TheirEntity {
return &TheirEntity{name: name}
}
// ... by embedding in MyEntity
type MyEntity struct {
*TheirEntity // However, I don't want to expose
// TheirEntity directly. How to embed this
// without exporting and not changing this
// to a named field?
color string
}
func (m MyEntity) PrintFavoriteColor() {
fmt.Println(m.color)
}
func NewMyEntity(name string, color string) *MyEntity {
return &MyEntity{
TheirEntity: NewTheirEntity(name),
color: color,
}
}
Since the question was asked, Go saw the addition of type aliases to the language with the 1.9 release in 2017. It turns out that, through an unconventional use of type aliases, you can have your cake and eat it too!
First, declare an unexported alias for the third-party type you wish to embed in your struct:
type theirEntity = TheirEntity
Then, simply embed that alias instead of the original type:
type MyEntity struct {
*theirEntity
color string
}
(Playground)
[I]s it possible to specify embedded structs as "private", even if they are imported from another package?
No.
How might one achieve the same thing in a more idiomatic fashion?
By not-embedding but making it a unexported named field.
Like this:
type MyEntity struct {
*privateTheirEntity
}
type privateTheirEntity struct {
*TheirEntity
}

Resources