The most cumbersome for newcomers to golang are the interfaces. Here are some examples of code I took apart to “smoke” them, I hope they will help you too. The idea is to look at the code, then close it and from memory try to write from scratch according to this plan:
- write the code through the usual functions
- turn functions into methods
- add an interface
Example number one:
Find the area and perimeter of a circle and rectangle from Go by Example:
package main import ("fmt"; "math") type geometry interface { area() float64 perim() float64 } type circle struct { radius float64 } type rect struct { width, height float64 } func (c circle) area() float64 { return math.Pi * c.radius * c.radius } func (c circle) perim() float64 { return 2 * math.Pi * c.radius } func (r rect) area() float64 { return r.width * r.height } func (r rect) perim() float64 { return (r.width + r.height) * 2 } func measure (g geometry) { fmt.Println(g.area()) fmt.Println(g.perim()) } func main() { r := rect {width: 3, height: 4} c := circle {radius: 5} measure(r) measure(c) }
Example number two – finding the area and perimeter of a circle and rectangle from book “An Introduction to Programming in Go” by Caleb Doxsey:
package main import ("fmt"; "math") type Circle struct { x, y, r float64 } type Rect struct { x1, y1, x2, y2 float64 } func distance(x1, y1, x2, y2 float64) float64 { a := x2 - x1 b := y2 - y1 return math.Sqrt(a*a + b*b) } func (c *Circle) area() float64 { return math.Pi * c.r * c.r } func (r *Rect) area() float64 { l := distance(r.x1, r.y1, r.x1, r.y2) w := distance(r.x1, r.y1, r.x2, r.y1) return l * w } func (c *Circle) perimeter() float64 { return 2 * math.Pi * c.r } func (r *Rect) perimeter() float64 { l := distance(r.x1, r.y1, r.x1, r.y2) w := distance(r.x1, r.y1, r.x2, r.y1) return (l + w) * 2 } type Shape interface { area() float64 perimeter() float64 } func totalArea (shapes ...Shape) float64 { var area float64 for _, s := range shapes { area += s.area() } return area } func totalPerimeter (shapes ...Shape) float64 { var perimeter float64 for _, s := range shapes { perimeter += s.perimeter() } return perimeter } func main() { c := Circle {0, 0, 5} r := Rect {0, 0, 10, 10} fmt.Println(c.area()) fmt.Println(r.area()) fmt.Println(c.perimeter()) fmt.Println(r.perimeter()) fmt.Println(totalArea(&c, &r)) fmt.Println(totalPerimeter(&c, &r)) } /* Example of interfaces' fields: type MultiShape struct { shapes []Shape } func (m *MultiShape) area() float64 { var area float64 for _, s := range m.shapes { area += s.area() } return area } */
Example number three: the animals from Jordan Orelli:
package main import ("fmt") type Animal interface { Speak() string } type Dog struct {} func (d Dog) Speak() string { return "Woof!" } type Cat struct {} func (c Cat) Speak() string { return "Meow!" } type Llama struct {} func (l Llama) Speak() string { return "???" } type JavaProgrammer struct {} func (j JavaProgrammer) Speak() string { return "Design patterns!" } func main() { animals := []Animal{ Dog{}, Cat{}, Llama{}, JavaProgrammer{} } for _, animal := range animals { fmt.Println(animal.Speak()) } }
And for those who want to delve into the interface hole properly, here’s article by Russ Cox.