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

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.

Related

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.

What is the best practice when multiple structs have several common functions in golang

I have 3 classes, which have several operation steps implemented in different functions. For example,
func ExternalAPI1() error
func ExternalAPI2(s string)
func internalProcess1() error
func internalProcess2(s []string) error
func internalProcess3(d int) error
For the internalProcessx, they're internal functions which may not be invoked by other classes, but these functions do have identical signatures.
Should I generate an interface for them with only ExternalAPI1 and ExternalAPI2 included? or should I include all these 5 functions in the interface?
tl;dr: don't worry about interfaces when creating structs, worry about interfaces when creating functions that would consume them.
The question overlooks an important underlying question: do you need an interface at all? I.e., is something going to accept values of the interface type, and operate on them without knowledge of the underlying type? If so, what methods will it call on the value? Those are the methods that should be in the interface.
Remember that Go uses duck interfaces; therefor, interfaces should be defined to suit the consumer, not the implementation. In fact, they should generally be defined alongside the consumer, not the implementation, because the implementation doesn't need to know anything about the interfaces it implements.
In Go, when a function or method can operate on "anything that implements method X", then alongside that function or method, you can define an "Xer" interface with that method signature. This allows anything to be passed to that function which meets the interface, and the function doesn't need to worry about what the underlying type is.
If you want the compiler to enforce that all structs which implement the interface have the unexported methods too, the unexported methods can certainly be included in the interface.
reflect.Type is an example from the stdlib (see here, godoc notes that the interface contains unexported methods)
An interface describes the methods required to perform a certain action. In your case, this is definitely the following:
type API interface {
ExternalAPI1() error
ExternalAPI2(s string)
}
However, do note that this does not limit the use of non-exported methods when using your struct (not class) directly from the same package.

Could anybody provide your-own/standard convention of "clone method" that work well for my/our reference?

I can't find a "clone" method convention in Golang, but it seems necessary to have one.
I only saw the built-in way *clonedObj = *obj, but it is too low-level, and can't handle (when-necessary) deep copy of case like struct { member *CompositionObj }
I doubt whether "func (obj ClassA) Clone() interface{}" prototype will work, because calling obj2 := obj.Clone() will "loose" the method set for ClassA, and need explicit code like obj2.(*ClassA) afterwards.
Please advice a working direction.
This answer to a similar question regarding maps suggests to use the gob package. The documentation states:
A stream of gobs is self-describing. Each data item in the stream is preceded by a specification of its type, expressed in terms of a small set of predefined types. Pointers are not transmitted, but the things they point to are transmitted; that is, the values are flattened. Nil pointers are not permitted, as they have no value. Recursive types work fine, but recursive values (data with cycles) are problematic. This may change.
so it may not be suitable for your use case.
That said, your question largely depends on your actual use-case. You do not need a generic way to deep-copy things usually, you can usually either get away with the built-in copy mechanics or write concrete copy functions for the types that actually need it.
An alternative might be the deepcopy package but I have no experience with it myself, I just found it on Goolge.
Ok, having some while no one else give me proper reference, I have found out some reference example how to clone in Go myself and want to share.
(Only upvote me a few if this answer is useful to you. I'm not for earning votes. Welcome other better answers and comments)
I found this protoype in package "github.com/jinzhu/gorm" (Database's ORM library) for reference:
func (s *DB) clone() *DB {
db := &DB{
...
}
...
return db
}
And similar pattern in package "golang.org/x/net/html/atom":
func (n *Node) clone() *Node {
m := &Node{
Type: n.Type,
...
}
...
return m
}
The above prototype is enough if the Clone()'s caller always know your object type when cloning. (and you need uppercase Clone() to make the method to be "public")
However, if you want advanced feature that a variable may hold any object of similar base interface, here is my sample:
func (t *T) Clone() YourBaseInterface
Where YourBaseInterface is:
type YourBaseInterface interface {
Clone() YourBaseInterface
OtherMethod1()
...
}
Or can merely use interface{} instead of YourBaseInterface in the return, and do a typecast like obj2 := obj.Clone().(*YourBaseType) after clone.
CAUTION
There is one drawback with this prototype. Becase Golang doesn't support this prototype as build-in, the Clone() method won't be called in some language's feature, e.g. when you copy(dest, src) a []YourTypeWithClone slice. Instead, it still do plain *elem2 = *elem1 struct copying. Solutions maybe either don't use those build-in, or you may flaw back to design the class struct members so that doing plain copy is enough for its copy purpose if possible.

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).

En/Decode struct containing many interfaces with different implementations each with gob

I have a quite complex struct that contains many interfaces with each different implementations. For en/decoding that struct in gob I seem to have to register every implementation that could be possibly used for every interface. So I end up with a method along these lines:
func registerImplementations() {
gob.Register(&Type1{})
gob.Register(&Type2{})
gob.Register(&Type3{})
gob.Register(&Type4{})
....
}
which I need to call before en/decoding. Is there an easier way to do this? Or should I look into possibilities for generating this method, since it's quite tedious to keep track of all possible implementations?
The documentation says:
We must register the concrete type for the encoder and decoder (which would
normally be on a separate machine from the encoder). On each end, this tells the
engine which concrete type is being sent that implements the interface.
So, at some point, you're going to want to call gob.Register, but you do want your code to be maintainable. This leaves (broadly) two options:
Creating a function like you're doing now, calling each struct after one another.
Advantage: all your Register-calls in a list, so you'll easily spot if you miss one, and you surely won't register one twice.
Disadvantage: you'll have to update it when using another implementation. You'll also have to call this function some time before encoding/decoding.
Creating something like this:
func Register(i interface{}) error {
gob.Register(i)
return nil
}
And then when writing a new implementation in your (let's say) dummy package, you can put this line below / above the interface declaration.
var regResult = reg.Register(myNewInterface{})
This will be called on startup (because it's global).
Advantage: not having to update the registerImplementations method.
Disadvantage: you'll have your registers all across your code (which can consist of a lot of files) - so you might miss one.
As to which is better: I'll leave that up to you.

Resources