We created complex data types (structs/classes), but what if we wanted to create another struct
that expands upon another? Or you wanted to create a number of different structs that play by a certain set of rules. The example code for today can be found here.
package main
import (
"fmt"
"math"
)
type Polygon interface {
Shape() string
Perimeter() float64
Area() float64
}
type Rectangle struct {
Length float64
Height float64
}
func (r Rectangle) Perimeter() float64 {
return 2*r.Height + 2*r.Length
}
func (r Rectangle) Area() float64 {
return r.Height * r.Length
}
func (r Rectangle) Shape() string {
return "rectangle"
}
func (r Rectangle) String() string {
return fmt.Sprintf("Shape: %s, Dimensions: %fx%f, Perimeter: %f, Area: %f", r.Shape(), r.Length, r.Height, r.Perimeter(), r.Area())
}
type ColoredRectangle struct {
Rectangle
Color string
}
func (r ColoredRectangle) String() string {
return fmt.Sprintf("Shape: %s, Dimensions: %fx%f, Perimeter: %f, Area: %f, Color: %s", r.Shape(), r.Length, r.Height, r.Perimeter(), r.Area(), r.Color)
}
type Triangle struct {
Sides [3]float64
}
func (t Triangle) Perimeter() float64 {
return t.Sides[0] + t.Sides[1] + t.Sides[2]
}
func (t Triangle) Area() float64 {
p := t.Perimeter() / 2
return math.Sqrt(p * (p - t.Sides[0]) * (p - t.Sides[1]) * (p - t.Sides[2]))
}
func (t Triangle) Shape() string {
return "triangle"
}
func (t Triangle) String() string {
return fmt.Sprintf("Shape: %s, Dimensions: %fx%fx%f, Perimeter: %f, Area: %f", t.Shape(), t.Sides[0], t.Sides[1], t.Sides[2], t.Perimeter(), t.Area())
}
func printSlice(s []interface{}) {
for i, v := range s {
fmt.Printf("%d - %v\n", i, v)
}
}
func main() {
rect := Rectangle{Length: 5, Height: 4}
tri := Triangle{Sides: [3]float64{4, 5, 6}}
fmt.Println(rect)
fmt.Println(tri)
var polys []Polygon
polys = append(polys, rect)
polys = append(polys, tri)
var cr ColoredRectangle
cr.Height = 7
cr.Length = 8
cr.Color = "red"
polys = append(polys, cr)
fmt.Println(polys)
for _, p := range polys {
fmt.Println(p)
}
fmt.Printf("Shape 1 type: %s\n", polys[0].Shape())
random := []interface{}{1, "blue", rect, tri}
printSlice(random)
if _, ok := random[1].(Polygon); !ok {
fmt.Println("Not a polygon")
}
fmt.Println(random[2].(Polygon).Shape())
}
Interfaces
An interface in general provides a way of communication between two or more parties. A GUI (Graphical User Interface) provides a means communication between a computer and a human. In the case of Golang and many other programming languages, an interface is a definition of how a struct
must behave in order to work with other portions of code that expect them to follow the definition. In our last lesson we created a struct
polygon
. It had a method to calculate the perimeter. What if we wanted to have it calculate area? Well, the area calculation varies from polygon to polygon. While there are a number of ways we could deal with this, let’s see how we could use interfaces to help.