151 views
# Go Interfaces - What are Go interfaces? - How are they different? - What can you do with them? - How do I use them effectively? πŸ‘‰ --- ## What are Go interfaces? - Interfaces are types. - You define a set of methods, - which defines its shape, - that concrete implementations satisfy. πŸ‘‡ ---- ## Example An contrived interface for defining the behavior of an animal: ```go type Animal interface { Speak() string } ``` πŸ‘‰ --- ## Conventions - Interfaces have an "adverb" as their name. - Methods are "verbs". Examples: - `Read()` and the `Reader` interface. - `Seek()` and the `Seeker` interface. However, naming is hardβ„’ so `ServeHTTP` for the `Handler` interface breaks this convention. in the [net/http](https://pkg.go.dev/net/http) package. πŸ‘‰ --- ## Best Practices - [Go Proverbs](https://go-proverbs.github.io/) - The bigger the interface the weaker the abstraction. - `interface{}` or `any` says nothing. πŸ‘‰ --- ## The empty interface Let's take a closer look at the empty interface: - `interface{}` -- Go 1.17 and earlier - `any` -- Go 1.18+ πŸ‘‡ ---- ### Example ```go package main import "fmt" type cat struct {} func (cat) Speak() string { return "Meow!" } func main() { var x any = &cat{} fmt.Printf("%T\n", x) } ``` πŸ‘‡ ---- ### Example (cont) - What does this print? - Let's have a look: https://go.dev/play/p/gLYPeeXZh8W - But wait?! [type any](https://pkg.go.dev/builtin#any) is the `interface{}` type?! 😱 - What's going on? πŸ‘‰ --- ## Type Assertions - Go converts concrete types to interfaces. - All types implement `any` or `interface{}`. - You can type assert a value with: `cat, ok := x.(Cat)`. - Now we can call `cat.Speak()` if `ok` is `true`. πŸ‘‰ --- ## Live Coding Time! - Let's live-code a more complex example... For those following along later, here's the code for [shapes](https://git.mills.io/prologic/shapes) as well as its [documentation](https://pkg.go.dev/git.mills.io/prologic/shapes). πŸ‘‰ --- ## Receivers Matter - Pointer Receiver vs. Value Receiver - Decide which one you need. - Go doesn't care or enforce this, but: You can end with compiler errors like this as seen on the [grow](https://git.mills.io/prologic/shapes/pulls/1) branch: ``` cannot use (Rectangle literal) (value of type Rectangle) as Shape value in variable declaration: Rectangle does not implement Shape (method Grow has pointer receiver) ``` πŸ‘‰ --- ## Interface Conformance Takes one of two forms: - `var _ InterfaceType = MyType{}` - `var _ InterfaceType = (*MyTyoe)(nil)` - πŸ’β€β™‚οΈ Put these at the top of your concrete types. πŸ‘‰ --- ## Embedding Interfaces - Interfaces can embed other interfaces. Example: ```go type Shape interface { fmt.Stringer ... } ``` See the [stringer](https://git.mills.io/prologic/shapes/pulls/3) branch. πŸ‘‰ --- ## Dependency Injection And of course, interfaces are used for dependency injection! Example: ```go func Stats(w io.Writer, shapes ...Shape) { for _, shape := range shape { _, _ := fmt.Fprintf(w, "%T Area(): %0.2f", shape, shape.Area()) } } ``` πŸ‘‰ --- ## The End 🎬 - Interfaces are very simple, - but very powerful! - Enables good testing, - and easier refactoring! - Promotes good reuse, - and architectural choices. Use them! πŸ‘Œ