Design pattern to avoid returning interfaces - go

My application has the following model that I simplified on purpose, and I think there is a lot of code smell in the way I am using interfaces:
Libraries have Collections
Collections have Items
Libraries know where the location if Item is.
Let's say I have ten libraries (that will be implemented in separated packages, each one with its specificities).
I want to manipulate the libraries in handlers to serve requests. So, I defined the following interfaces:
type Library interface {
ListCollections(prefix string) []Collection
GetItemLocation(item Item) string
}
type Collection interface {
SearchItems(query string) []Item
}
type Item interface {
Name() string
Summary() string
}
I cannot define an Item struct as each one is different. However, with respect to the outer world, having the Item interface is sufficient.
As you can see, I have methods returning interfaces and I don't really like it. Moreover, I struggle with, in this example, the GetItemLocation part: it expects an Item, but I need to access fields of the Item (specific to each collection) so it forces me to type cast the argument inside the method.
Which patterns could I use to breakdown this complexity and write more diomatic go code?

Related

Using msgp with interfaces and maps in Go

I have a map that uses an interface as the key. The map is defined like this MyMap map[Signature]Packets. The interface is Signature, and there will be two structs A and B that implement this interface. I am also using msgp to serialize these two structs.
My issue is that msgp automatically generates methods that use a pointer as the type of the function receiver, which I think will make the key Signature receive pointers. If that was the case, then the key would be different every single time since pointers are different, even though the underlying values are the same. So, every time, I would be creating a new entry instead of finding the existing one and modifying it.
I wonder:
Is there a way to force msgp to generate methods purely with function receivers of the concrete type? Currently, I can only modify the function receivers of auto-generated methods like MarshalMsg and UnmarshalMsg to the concrete type (A or B instead of *A or *B). By doing that, the key of the map is either of type A or of type B, and the map MyMap works fine. However, I know I should not modify the auto-generated code. So, I wonder whether there is an acceptable way to do that.
If there is no way to do 1., is there any workaround to solve this problem? I really need some polymorphic feature of the map's key with the use of msgp.
UPDATE 1 (Apr. 12):
Thanks for sharing your thoughts and offering solutions. Here are some details about my question.
The background is that the map is used for collecting different network events. The two structs implementing the interface Signature are EventSignatureIPv4 and EventSignatureIPv6
type EventSignatureIPv4 struct {
SourceIPv4 [4]byte
Port uint16
Traffic TrafficType
}
type EventSignatureIPv6 struct {
SourceIPv6 [16]byte
Port uint16
Traffic TrafficType
}
and Signature is holding common methods shared between IPv4 and IPv6 data. So, essentially, I want to collect and group corresponding IPv4/v6 events at the runtime. The key of the map is to identify the same source, and the value of the map is to collect events with different destinations.
The msgp library I am using is this one https://pkg.go.dev/github.com/tinylib/msgp#v1.1.5/msgp
Correct me if I am wrong. For compositions in Go, if one of the methods in the method set has a function receiver of the pointer type, then the instance would only be of the pointer type? So here, as I have
func (z *EventSignatureIPv6) MarshalMsg(b []byte) (o []byte, err error) {
/* Auto-generated code */
}
whenever I use Signature to receive the struct EventSignatureIPv6, the struct would only be of type *EventSignatureIPv6?
You are right, "two pointer values are equal if they point to the same variable.", so if you are looking to compare interfaces that may hold pointers to different types, e.g. *A and *B, you are already in trouble.
With that said, I don't think it's an amazing idea to use interface types as map keys in the first place, because you have to deal with some caveats, the first is that:
The comparison operators == and != must be fully defined for operands of the key type
And now you need to be careful about the types that implement the interface. In theory, nobody stops a client from implementing your interface on a defined type with underlying unhashable type, e.g. type UncomparableSignature []int
So you would probably have to add an unexported method on your interface, so that client code outside that package can't implement it. But still, nothing stops code within the same package from implementing it, so this is, at best, maintenance overhead.
Then if the interface holds pointers to zero-values, it's even dependant on the implementation of the specs:
Pointers to distinct zero-size variables may or may not be equal.
Furthermore, you open yourself up to pesky bugs, like variables of type Signature that holds a nil will overwrite each other's values:
var foo Signature
var bar Signature
myMap[foo] = &Packet{/*pretending to have value 1*/}
myMap[bar] = &Packet{/*pretending to have value 2*/}
fmt.Println(myMap[foo]) // 2
A possible solution is, you could replace the map key with a unique id, and you enforce implementors to provide it by declaring the appropriate method on the interface Signature (this still assumes that the implementors can be coordinated to provide unique ids across all of them):
type Signature interface {
UniqueIdent() uint64 // or string, if you prefer
// ...other methods
}
and then
packet := myMap[someSignature.UniqueIdent()]

How to write idiomatic constructor

I'm confused about the constructors in Go. Most constructors I've seen return a struct, but 'Effective Go' suggests that an interface can be returned in some cases, according to the rule of 'Generality'.
I trust 'Effective Go' to provide good ideas, but this doesn't seem to follow the principle of 'accept interfaces, return structs'. I guess that many types implement an interface and nothing more than that, so in that case it would be common to see constructors which return interfaces.
Another related statement is that interfaces should be defined by the consumer, but 'Generality' means that the interface is defined by the producer.
Can someone clarify?
As it has already been mentioned, returning an interface should be considered something exceptional.
Returning errors of type error which is an interface is one of those exception.
Returning an interface that represents an unexported type is the other exception. But why would you have an exported interface that describes an unexported struct instead of just having an exported struct?
The reason is simple, that allows you a higher degree of control on how that struct is constructed.
Compare this two pieces of code:
type MyType struct {
MyField string
}
func NewMyType(value string) MyType {
return MyType{value}
}
func (t MyType) MyMethod() string {
return t.MyField
}
type MyType interface {
MyMethod() string
}
type myType struct {
MyField string
}
func NewMyType(value string) MyType {
return myType{value}
}
func (t myType) MyMethod() string {
return t.MyField
}
In the first case I would be able to do: myVar := MyType{} while in the second case I won't be able to do so, I am forced to use the provided constructor. The first case also allows to modify the field value after creation which is not allowed in the second case. Making the field unexported will solve the second part but not the first.
This example is obviously trivial, but being able to construct invalid structs may have a horrible impact. By having specific constructors you can ensure that the object is in a valid starting state and you will only need to make sure it always stays in a valid state. If you can't ensure that, you may need to check that it is in a valid state at the start of every method.
For example consider a DB request. It needs a DB connection. If the user is able to create a DB request without a DB connection you will have to check that it is valid in every method. If you enforce him to use a constructor you can check at creation time and done.
It depends a bit on your preference and how you view things. Coming from OOP background my take is: there is no point in the constructor if you cannot enforce it. Adding the constructor means - you must supply these values when instantiating this item. If your struct is public, it will be misused and instantiated bypassing the constructor. So it makes sense that the constructor returns the public interface and the struct is private (lowercase). If the struct is public, there is no point in the constructor, because you cannot enforce it. Writing code is a dialogue between writer and reader, making a struct public and having a constructor would tell the reader - here you have the constructor, but you also have a public struct and that would mean that constructor usage is arbitrary. If that is the case, go with that setup
In most cases constructor functions return concrete types (or pointer to a type).
The situations in which returning interfaces might be a good idea is when calling factory functions or builder functions in which underlying concrete type satisfies that interface.
Consider error interface for example, when you call http.NewRequest underlying concentrate error type can be of net.Error, net.DNSError etc. Now try to think how are you going to create an api like this without an error interface if function returns concrete type? Only solution to it I can think of is to create a massive error type for net package and add fields for extra information, but its most probably much harder to maintain, test that kind of error type and not to mention memory bloat.
Whether you choose to return concrete type or an interface is a design choice, some guidelines exists to give solution to common scenarios.

How to avoid a golang function having different behaviors between calling the embedding and embedded types?

Let's say in a 3rd party library we have an interface and a struct implementing this interface. Let's also assume there is a function that takes ParentInterface as argument, which have different behavior for different types.
type ParentInterface interface {
SomeMethod()
}
type ParentStruct struct {
...
}
func SomeFunction(p ParentInterface) {
switch x := p.Type {
case ParentStruct:
return 1
}
return 0
}
In our code we want to use this interface, but with our augmented behavior, so we embed it in our own struct. The compiler actually allows us to call functions about ParentInterface on my struct directly:
type MyStruct struct {
ParentInterface
}
parentStruct := ParentStruct{...}
myStruct := MyStruct{parentStruct}
parentStruct.SomeMethod() // Compiler OK.
myStruct.SomeMethod() // Compiler OK. Result is same. Great.
SomeFunction(parentStruct) // Compiler OK. Result is 1.
SomeFunction(myStruct.ParentInterface) // Compiler OK. Result is 1.
SomeFunction(myStruct) // Compiler OK. Result is 0. (!)
Isn't the last case a problem? I've encountered this kind of bugs more than once. Because I'm happily use MyStruct as an alias of ParentInterface in my code (which is why I define it in the first place), it's so hard to always remember that we cannot call SomeFunction on MyStruct directly (the compiler says we can!).
So what's the best practice to avoid this kind of mistake? Or it's actually a flaw of the compiler, which is supposed to forbid the use of SomeFunction(myStruct) at all since the result is untrustable anyway?
There is no compiler mistake here and your experienced result is the expected one.
Your SomeFunction() function explicitly states it wants to do different things based on the dynamic type of the passed interface value, and that is exactly what happens.
We introduce interfaces in the first place so we don't have to care about the dynamic type that implements it. The interface gives us guarantees about existing methods, and those are the only things you should rely on, you should only call those methods and not do some type-switch or assertion kung-fu.
Of course this is the ideal world, but you should stick to it as much as possible.
Even if in some cases you can't fit everything into the interface, you can again type assert another interface and not a concrete type out of it if you need additional functionality.
A typical example of this is writing an http.Handler where you get the response writer as an interface: http.ResponseWriter. It's quite minimalistic, but the actual type passed can do a lot more. To access that "more", you may use additional type assertions to obtain that extra interface, such as http.Pusher or http.Flusher.
In Go, there is no inheritance and polymorphism. Go favors composition. When you embed a type into another type (struct), the method set of the embedded type will be part of the embedder type. This means any interfaces the embedded type implemented, the embedder will also implement those. And calling methods of those implemented interfaces will "forward" the call to the embedded type, that is, the receiver of those method calls will be the embedded value. This is unless you "override" those methods by providing your own implementation with the receiver type being the embedder type. But even in this case virtual routing will not happen. Meaning if the embedded type has methods A() and B(), and implementation of A() calls B(), if you provide your own B() on the embedder, calling A() (which is of the embedded type) will not call your B() but that of the embedded type.
This is not something to avoid (you can't avoid it), this is something to know about (something to live with). If you know how this works, you just have to take this into consideration and count with it.
Because I'm happily use MyStruct as an alias of ParentInterface in my code (which is why I define it in the first place)
You shouldn't use embedding to create aliases, that is a misuse of embedding. Embedding a type in your own will not be an alias. Implementations of existing methods that check concrete types will "fail" as you experienced (meaning they will not find a match to their expected concrete type).
Unless you want to "override" some methods or implement certain interfaces this way, you shouldn't use embedding. Just use the original type. Simplest, cleanest. If you need aliases, Go 1.9 introduced the type alias feature whose syntax is:
type NewType = ExistingType
After the above declaration NewType will be identical to ExistingType, they will be completely interchangeable (and thus have identical method sets). But know that this does not add any new "real" feature to the language, anything that is possible with type aliases is doable without them. It is mainly to support easier, gradual code refactoring.

Can I create an alias of a type in Golang?

I'm struggling with my learning of Go.
I found this neat implementation of a Set in go: gopkg.in/fatih/set.v0, but I'd prefer naming my sets with a more explicit name that set.Set, doing something like:
type View set.Set
In essence, I want my View type to inherit set.Set's methods. Because, well, View is a set.Set of descriptors. But I know Go is pretty peaky on inheritance, and typing in general.
For now I've been trying the following kinda inheritance, but it's causing loads of errors when trying to use some functions like func Union(set1, set2 Interface, sets ...Interface) Interface or func (s *Set) Merge(t Interface):
type View struct {
set.Set
}
I'd like to know if there's a way to achieve what I want in a Go-like way, or if I'm just trying to apply my good-ol' OO practices to a language that discards them, please.
If anyone else is coming back to this question, as of Go 1.9 type aliases are now supported.
A type alias has the form: type T1 = T2
So in your example you can just do type View = set.Set and everything will work as you want.
Note, I think the simple aliasing you proposed initially is syntactically valid though having had a quick look at the set library, rather than aliasing set.Set it might make more sense to alias set.Interface, e.g.:
package main
import (
"fmt"
set "gopkg.in/fatih/set.v0"
)
// View is a type alias for the set.Interface interface
type View set.Interface
// Display takes one of our View types in order to print it.
func Display(view View) {
fmt.Println(view.List())
}
func main() {
// create our first set.Interface or View
v1 := set.New()
v1.Add("foo")
// create our second set.Interface or View
v2 := set.New("bar")
// call a set function
v3 := set.Union(v1, v2)
// call our function that takes a View
Display(v3)
}
You may have noticed I'm cheating somehow because I make no real mention of the aliased type in the above code other than in defining the parameter to the Display function above which you'll note takes in a View instance rather than a set.Interface. If you have lots of functions working on these things, then that might read more expressively for your domain.
Note that because our View type is an alias to an interface type, it precludes adding your own functions to that type as Go doesn't allow us to have an interface receiver type for a function (I might be expressing that incorrectly). By this I mean that you can't do anything like:
func (v View) Display() string {
return v.String()
}
In summary I think aliasing things is fine, it can make internal APIs more readable, and you can lean on the compiler to help eliminate certain classes of errors; however this doesn't allow you to add functionality to the custom type. If this is required an alternate approach would be necessary, either embedding or simple composition (i.e. a View has a Set).

Designing Go packages: when I should define methods on types?

Suppose that I have a type type T intand I want to define a logic to operate on this type.
What abstraction should I use and When ?
Defining a method on that type:
func (T t) someLogic() {
// ...
}
Defining a function:
func somelogic(T t) {
// ...
}
Some situations where you tend to use methods:
Mutating the receiver: Things that modify fields of the objects are often methods. It's less surprising to your users that x.Foo will modify X than that Foo(x) will.
Side effects through the receiver: Things are often methods on a type if they have side effects on/through the object in subtler ways, like writing to a network connection that's part of the struct, or writing via pointers or slices or so on in the struct.
Accessing private fields: In theory, anything within the same package can see unexported fields of an object, but more commonly, just the object's constructor and methods do. Having other things look at unexported fields is sort of like having C++ friends.
Necessary to satisfy an interface: Only methods can be part of interfaces, so you may need to make something a method to just satisfy an interface. For example, Peter Bourgon's Go intro defines type openWeatherMap as an empty struct with a method, rather than a function, just to satisfy the same weatherProvider interface as other implementations that aren't empty structs.
Test stubbing: As a special case of the above, sometimes interfaces help stub out objects for testing, so your stub implementations might have to be methods even if they have no state.
Some where you tend to use functions:
Constructors: func NewFoo(...) (*Foo) is a function, not a method. Go has no notion of a constructor, so that's how it has to be.
Running on interfaces or basic types: You can't add methods on interfaces or basic types (unless you use type to make them a new type). So, strings.Split and reflect.DeepEqual must be functions. Also, io.Copy has to be a function because it can't just define a method on Reader or Writer. Note that these don't declare a new type (e.g., strings.MyString) to get around the inability to do methods on basic types.
Moving functionality out of oversized types or packages: Sometimes a single type (think User or Page in some Web apps) accumulates a lot of functionality, and that hurts readability or organization or even causes structural problems (like if it becomes harder to avoid cyclic imports). Making a non-method out of a method that isn't mutating the receiver, accessing unexported fields, etc. might be a refactoring step towards moving its code "up" to a higher layer of the app or "over" to another type/package, or the standalone function is just the most natural long-term place for it. (Hat tip Steve Francia for including an example of this from hugo in a talk about his Go mistakes.)
Convenience "just use the defaults" functions: If your users might want a quick way to use "default" object values without explicitly creating an object, you can expose functions that do that, often with the same name as an object method. For instance, http.ListenAndServe() is a package-level function that makes a trivial http.Server and calls ListenAndServe on it.
Functions for passing behavior around: Sometimes you don't need to define a type and interface just to pass functionality around and a bare function is sufficient, as in http.HandleFunc() or template.Funcs() or for registering go vet checks and so on. Don't force it.
Functions if object-orientation would be forced: Say your main() or init() are cleaner if they call out to some helpers, or you have private functions that don't look at any object fields and never will. Again, don't feel like you have to force OO (à la type Application struct{...}) if, in your situation, you don't gain anything by it.
When in doubt, if something is part of your exported API and there's a natural choice of what type to attach it to, make it a method. However, don't warp your design (pulling concerns into your type or package that could be separate) just so something can be a method. Writers don't WriteJSON; it'd be hard to implement one if they did. Instead you have JSON functionality added to Writers via a function elsewhere, json.NewEncoder(w io.Writer).
If you're still unsure, first write so that the documentation reads clearly, then so that code reads naturally (o.Verb() or o.Attrib()), then go with what feels right without sweating over it too much, because often you can rearrange it later.
Use the method if you are manipulating internal secrets of your object
(T *t) func someLogic() {
t.mu.Lock()
...
}
Use the function if you are using the public interface of the object
func somelogic(T *t) {
t.DoThis()
t.DoThat()
}
if  you want to change T object, use
func (t *T) someLogic() {
// ...
}
if you donn't change T object and would like a origined-object way , use
func (t T) someLogic() {
// ...
}
but remeber that this will generate a temporay object T to call someLogic
if your like the way c language does, use
func somelogic(t T) {
t.DoThis()
t.DoThat()
}
or
func somelogic(t T) {
t.DoThis()
t.DoThat()
}
one more thing , the type is behide the var in golang.

Resources