How to create abstract structure implementations [duplicate] - go

This question already has answers here:
Implementing an interface with a wider method signature
(3 answers)
How to implement interface method with return type is an interface in Golang
(1 answer)
Closed 4 months ago.
The community reviewed whether to reopen this question 4 months ago and left it closed:
Original close reason(s) were not resolved
I've got a slice of steps []*Steps where each step has a type and a custom payload depending on it:
type Step struct {
ID string
Name string
Type StepType // Enum: StepA | StepB
Action StepAction // interface implemented by either ActionStepA | ActionStepB
// Other common fields
}
// StepAction is an interface implemented by either ActionStepA | ActionStepB
type StepAction interface {}
type ActionStepA struct {FieldA string} // Fields specific to step A
type ActionStepB struct {FieldB string} // Fields specific to step B
Depending on the Type, the Action will be a different struct: either ActionStepA or ActionStepB which just hold their own step-specific fields.
To Execute these steps, I have a StepExecutor interface:
type StepExecutor interface {
Execute(StepAction) error
}
Ideally, I'd like to have a StepExecutor implementation per StepType which contains the business logic and knows how to execute its type. E.g. have StepAExecutor() which executes ActionStepA and StepBExecutor which executes ActionStepB.
How can I achieve something like this in GOlang? I guess what I need is a concept similar to Abstract Classes?
for _, step := range steps {
executor := getExecutorForStep(step) // returns a StepExecutor type
executor.Execute(step.Action)
}
// ERROR HERE:
fun getExecutorForStep(step *Step) StepExecutor {
switch step.Type {
case StepA: return &StepAExecutor{}
case StepB: return &StepBExecutor{}
}
}
I tried the above but I'm getting errors as the StepExecutor "implementations" are not actually implementations as they have mismatched method signatures:
StepExecutor expects StepAction for the Execute method
StepAExecutor expects ActionStepA for the Execute method
StepBExecutor expects ActionStepB for the Execute method
If go doesn't allow for such a case, I'm happy to redo the architecture with a proper more common one. Let me know if you have any suggestions. I understand that in golang, methods signatures must match literally - I'm just unsure how I can properly structure this problem. I might be in need of a re-architecture.

Related

Why can't I assign a embedded struct to a parent struct in go?

I have below code try to assign embed struct to its parent struct. There are two set of structure: Guider is the parent struct, DataBlock extends from it. The method func Put(x Guider) accept a parameter with type Guider. It works when I pass a DataBlock variable.
However, the other case is Mock extends from zerolog.Event, but it fails to pass the parameter on the method Test(e zerolog.Event)
I got the following error:
cannot use m (variable of type Mock) as type zerolog.Event in argument to Test
Why are these two cases works differently? How can I make them both work?
package main
import (
"fmt"
"github.com/rs/zerolog"
)
type Guider interface {
Guid() string
}
type FSEntity struct {
guid string
}
func (e FSEntity) Guid() string {
return e.guid
}
func Put(x Guider) {
fmt.Printf("%+v\n", x)
}
type Mock struct {
zerolog.Event
}
func Test(e zerolog.Event) {
}
//Child struct:
type DataBlock struct {
FSEntity
data []byte
}
func main() {
myVar := DataBlock{}
myVar.guid = "test"
myVar.data = []byte("moar test")
Put(myVar) // it works
m := Mock{}
Test(m) // it doesn't work. cannot use m (variable of type Mock) as type zerolog.Event in argument to Test
}
First, a couple of definitions:
Polymorphism
Polymorphism is the provision of a single interface to entities of different types or the use of a single symbol to represent multiple different types.
Subtyping
Subtyping (also subtype polymorphism or inclusion polymorphism) is a form of type polymorphism in which a subtype is a datatype that is related to another datatype (the supertype) by some notion of substitutability, meaning that program elements, typically subroutines or functions, written to operate on elements of the supertype can also operate on elements of the subtype
Inheritance
In object-oriented programming, inheritance is the mechanism of basing an object or class upon another object (prototype-based inheritance) or class (class-based inheritance), retaining similar implementation.
Object composition
Object composition and object aggregation are closely related ways to combine objects or data types into more complex ones.
Golang follows composition over inheritance principle, e.g. it doesn't support inheritance. So when you're saying
Mock extends from zerolog.Event
you actually mean that Mock includes zerolog.Event struct.
The way Golang implements polymorphism is interface. All types that implement some interface can be used in its place. It's what you see when use Guider.
However, it doesn't work for simple structs. zerolog.Event is a struct inside Mock.
So, normally, Test function should accept some interface as a parameter, and both mock and real event should implement this interface. However, it looks like zerolog doesn't provide interface for Event. So instead you should access the Event field of you struct. Example
Put(myVar) is legal because myVar is a DataBlock which contains (not inherits from and not implements) an FSEntity which in turn implements the Guider interface.
Since Put accepts a Guider, the reference to myVar is compatible, by virtue of the anonymous FSEntity field it contains which implements Guider. The implementation of Guider on FSEntity is (in effect) elevated to the containing struct (providing a means of delegating interfaces). This only occurs if the contained field is anonymous.
But in the case of Test(m), the function accepts a zerolog.Event which is a struct type, not an interface. As such, there is no "delegation" possible. Test() must be passed a zerolog.Event and in this scenario, this requires that you use the type name of the anonymous field:
Type(m.Event)
Some bonus info:
If DataBlock contained two anonymous fields which both implemented Guider then implicit delegation/elevation cannot take place; golang does not know which of the contained implementations should be delegated to/elevated (if any). In that scenario you must again use the name of the field that you wish to pass to the Put() function:
// given...
type Foo string
func (f Foo) Guid() string {
return string(f)
}
// and...
type DataBlock struct {
FSEntity
Foo
data []byte
}
// then...
Put(myVar) // is now illegal
// and must instead use either/or:
Put(myVar.FSEntity)
Put(myVar.Foo)
Whether implicit or explicit, the crucial distinction is that it is a field of the DataBlock (myVar) that is passed to Put(), not myVar itself.
If you want to pass the DataBlock to Put(), using a Guider interface, then DataBlock must itself implement the Guider interface.
Take this with a grain of salt, since I'm not familiar with zerolog package.
Your Guider is an interface, which might have any underlying type as long as Guid() method is satisfied. I assume this is happening through DataBlock containing FSEntity, which itself implements Guid() method, therefore satisfies MIGHT the interface.
On the other hand, I don't know what methods should be implemented to satisfy zerolog.Event or if it's even an interface, or a struct straight up. If it's an interface, you might need to implement it's required methods to be able to use DataBlock as zerolog.Event type. You might want/need to dig into that direction for some very specific answers.

Is there a graceful approach to deserializing Go interface types? [duplicate]

This question already has answers here:
Can I unmarshal JSON into implementers of an Interface?
(2 answers)
Closed 11 months ago.
The community reviewed whether to reopen this question 11 months ago and left it closed:
Original close reason(s) were not resolved
Given the following:
type Foo struct {
Td ThingDoer
// ... other stuff
}
type ThingDoer interface {
doThing()
}
type doerA struct {
AGuts string
}
func (a doerA) doThing() {}
type doerB struct {
BGuts string
}
func (b doerB) doThing() {}
is there a preferred serialization / deserialization strategy for Foo?
Attaching, eg, a MarshalJSON function onto doerA and doerB satisfies the serialization, but then Foo.UnmarshalJSON is effectively stuck: it can't know in advance whether the supplied JSON is of doerA or doerB type.
Edit: The linked "similar" question addresses the specific non-solution example outlined in this question. This question is asking about the existence of a graceful solution.
Imagine you have an structure Foo with one or more fields using interface types.
Lets say you have an interface Bar with two possible structures: Baz and Bam.
You can define auxiliary type (FooConf), without any interface. Only concrete types.
This structure may have a method Build() Foo that will choose the right type on each case.
To be possible define what is the concrete type you can define a signature. For instance an extra field “type” (baz or bam).
You just need to be sure about each type can marshal/unmarshal with consistency.

How do you access this Go struct? [duplicate]

This question already has an answer here:
Strange type definition syntax in Golang (name, then type, then string literal)
(1 answer)
Closed 1 year ago.
I'm trying to make sense of this Go struct:
type ListClustersOutput struct {
_ struct{} `type:"structure"`
// A list of all of the clusters for your account in the specified Region.
Clusters []*string `locationName:"clusters" type:"list"`
// The nextToken value to include in a future ListClusters request. When the
// results of a ListClusters request exceed maxResults, you can use this value
// to retrieve the next page of results. This value is null when there are no
// more results to return.
NextToken *string `locationName:"nextToken" type:"string"`
}
Looking at the docs: https://golangdocs.com/structs-in-golang#defining-a-struct-in-go
it gives an example:
type Fruit struct {
name string
}
which seems very different.
In the more complex code, I assume this Clusters []*string `locationName:"clusters" type:"list"` is equivalent to name string but struggling to unpack it.
I'm struggling to find much out about type: "list" - most of the examples seem to refer to slices. Why are they using a list?
what is a locationName?
how do you access the first element of the list in that struct?
Note, for this last question, if I use result.Clusters[0] (where result is of this struct type) I get a pointer. E.g.
fmt.Println("Result: ", result.Clusters[0])
Result: 0xc000372260
How do I dereference it?
Looking at this:
How does pointer dereferencing work in Go?
it seems you need an asterisk or an ampersand. Not clear which one you use or whether you tack it on the beginning or the end.
You are struggling with struct tags.
In your code:
Clusters []*string `locationName:"clusters" type:"list"`
The Clusters field has a type ([]*string) and the rest of the declaration are 2 struct tags that you should take the value(s) of tags using struct tag.
Here's how you access it:
fmt.Println("Result: ", *result.Clusters[0])

how to decode a byte slice to different structs elegantly

var response Response
switch wrapper.Domain {
case "":
response = new(TypeA)
case "TypeB":
response = new(TypeB)
case "TypeC":
response = new(TypeC)
case "TypeD":
response = new(TypeD)
}
_ = decoder.Decode(response)
As shown in the code snippet, I got enough information from the Domain filed of wrapper to determine the type of response, and for each type, the following operations are performed:
create a new instance of that type using new
use the decoder to decode the byte slice to the instance created in step 1
I am wondering if there is a way to make the first step more generic and get rid of the switch statement.
A bit about your code
As per discussion in comments, I would like to share some experience.
I do not see nothing bad in your solution, but there are few options to improve it, depends what you want to do.
Your code looks like classic Factory. The Factory is a pattern, that create object of a single family, based on some input parameters.
In Golang this is commonly used in simpler way as a Factory Method, sometimes called Factory function.
Example:
type Vehicle interface {};
type Car struct {}
func NewCar() Vehicle {
return &Car{}
}
But you can easily expand it to do something like you:
package main
import (
"fmt"
"strings"
)
type Vehicle interface {}
type Car struct {}
type Bike struct {}
type Motorbike struct {}
// NewDrivingLicenseCar returns a car for a user, to perform
// the driving license exam.
func NewDrivingLicenseCar(drivingLicense string) (Vehicle, error) {
switch strings.ToLower(drivingLicense) {
case "car":
return &Car{}, nil
case "motorbike":
return &Motorbike{}, nil
case "bike":
return &Bike{}, nil
default:
return nil, fmt.Errorf("Sorry, We are not allowed to make exam for your type of car: \"%s\"", drivingLicense)
}
}
func main() {
fmt.Println(NewDrivingLicenseCar("Car"))
fmt.Println(NewDrivingLicenseCar("Tank"))
}
Above code produces output:
&{} <nil>
<nil> Sorry, We are not allowed to make exam for your type of car: "Tank"
So probably you can improve your code by:
Closing into a single function, that takes a string and produces the Response object
Adding some validation and the error handling
Giving it some reasonable name.
There are few related patterns to the Factory, which can replace this pattern:
Chain of responsibility
Dispatcher
Visitor
Dependency injection
Reflection?
There is also comment from #icza about Reflection. I agree with him, this is used commonly, and We cannot avoid the reflection in our code, because sometimes things are so dynamic.
But in your scenario it is bad solution because:
You lose compile-time type checking
You have to modify code when you are adding new type, so why not to add new line in this Factory function?
You make your code slower(see references), it adds 50%-100% lose of performance.
You make your code so unreadable and complex
You have to add a much more error handling to cover not trivial errors from reflection.
Of course, you can add a lot of tests to cover a huge number of scenarios. You can support TypeA, TypeB, TypeC in your code and you can cover it with tests, but in production code sometime you can pass TypeXYZ and you will get runtime error if you do not catch it.
Conclusion
There is nothing bad with your switch/case scenario, probably this is the most readable and the easiest way to do what you want to do.
Reference
Factory method: https://www.sohamkamani.com/golang/2018-06-20-golang-factory-patterns/
Classic book about patterns in programming: Design Patterns: Elements of Reusable Object-Oriented Software, Erich Gamma and his band of four, ISBN: 978-0201633610
Reflection benchmarks: https://gist.github.com/crast/61779d00db7bfaa894c70d7693cee505

Go Functions/Methods without a name

I'm really struggling to find a name for a type of function I've come across.
Here is the function in question:
https://github.com/go-fsnotify/fsnotify/blob/master/fsnotify.go#L32
This is how I'm using it (as per the fsnotify example):
select {
case event := <-watcher.Events:
log.Println("Event Triggered: ", event)
In that Println 'event' is returning the formatted string as per the function above, I'm just struggling to understand how a straight call to 'event' is using that function yet I would be expecting it to be accessed like the struct fields (event.Name, event.Op):
event.funcForReturningNicelyFormattedEvent()
It feels like this is a 'default' function as it has no name and it just returns the formatted data - I'm struggling to come up with the name/type/search term so I can find out more and understand the concept and importantly the reasoning behind it better.
Any help is appreciated.
It's very simple - println uses the String() method on any struct that implements it automatically. This is a classic use case of Go's implicit interfaces: every struct that has the methods an interface includes, is considered to be implementing the interface.
If it has func String() string it is considered a Stringer and used by fmt. You can use it on your own structs too, of course.
Function Println checks if the passed value implements interface Stringer. If it does it calls method String on this value. Event type implements that interface by supplying its implementation of String method in the excerpt you linked to.
In Go you don't have to declare that you implement interface.

Resources