If Go's interfaces aren't enforced, are they necessary?
The Go language has interface types as features, analogous to C-style interfaces. Go's interface types, though, don't seem to be enforced -- they merely define protocol without actually being applied to a type. As they are not enforced, is it still a good idea to use interfaces?
Solution 1:
Yes. Go doesn't allow you to build type-hierarchies, so interfaces are very important to allow some polymorphism. Consider the sort.Interface
defined in the package sort
:
type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less returns whether the element with index i should sort
// before the element with index j.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}
The sort
package contains a function sort(data Interface)
that expects any object that implements this interface. Without interfaces, such form of polymorphism would not be possible in go. The fact that you don't have to explicitly annotate that your type implements this interface, is irrelevant.
The cool part about go is that you can even implement this interface on primitive types, as long as the type is defined in the same package. So the following code defines a sortable array of integers:
type Sequence []int
// Methods required by sort.Interface.
func (s Sequence) Len() int {
return len(s)
}
func (s Sequence) Less(i, j int) bool {
return s[i] < s[j]
}
func (s Sequence) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
Solution 2:
Yes, it is still an excellent idea to use interfaces. Go emphases interfaces. See https://talks.golang.org/2014/go4gophers.slide#5. Extending @ElianEbbing outstanding answer, methods on any types and ad hoc interfaces make for a light-weight OO programming style. See https://talks.golang.org/2012/goforc.slide#48.
An example might illustrate this.
package main
import . "fmt"
func main() {
c.Cry() // Meow
Cryer.Eat(c) // Fish
}
var c = cat{"Fish"}
type cat struct{ s string }
type Eater interface{ Eat() }
type Cryer interface {
Cry()
Eater
}
func (c cat) Eat() { Println(c.s) }
func (cat) Cry() { Println("Meow") }
The Cryer
interface lets you access both the Cry()
and Eat()
methods. See https://talks.golang.org/2012/zen.slide#16. You can add Cryer
to the underlying code without its alteration. See https://stackoverflow.com/a/11753508/12817546. And the code works fine if we remove it. You can still access a method directly as shown by c.Cry()
. This is because Go lets you do two things.
First, you can implement an interface implicitly. An interface is simply a set of methods. See https://talks.golang.org/2013/go4python.slide#33. An interface variable can store any non-interface value as long as it implements the interface's methods. See https://blog.golang.org/laws-of-reflection. Values of any type that implement all methods of an interface can be assigned to a variable of that interface. See https://talks.golang.org/2014/taste.slide#20.
c
is a variable of cat
. cat
implements the Eat()
and Cry()
methods. Eater
explicitly implements Eat()
. Cryer
explicitly implements Cry()
and Eat()
. Therefore cat
implicitly implements the Eater
and Cryer
interface. See https://talks.golang.org/2012/goforc.slide#40. Thus c
a variable of cat
can be a variable of Eater
and Cryer
. See https://golang.org/doc/effective_go.html#blank_implements.
Second, struct and interface types can be embedded in other struct and interface types. See https://golang.org/doc/effective_go.html#embedding. Cryer.Eat(c)
calls Eat()
since it embeds Eater
. Interfaces in Go are thus the primary means for decoupling components of our programs. See https://www.reddit.com/r/golang/comments/6rwq2g. And why interfaces can provide a nice API between packages, clients or servers. See https://stackoverflow.com/a/39100038/12817546.
If you don’t see a benefit, then don’t add an interface. See the comment in https://stackoverflow.com/a/39100038/12817546. There is no explicit hierarchy and thus no need to design one! See https://talks.golang.org/2012/goforc.slide#46. Add an interface when you need it. Define your data types first and build your 1-3 method(s) interface
as you go along. See https://stackoverflow.com/a/11753508/12817546. Quotes edited to match example.