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.