Chapter 11 - Interfaces

Exercise 1: Interfaces

Here we have Cuboid and Sphere types, both of which have Volume and SurfaceArea methods. We also have a PrintInfo function that accepts a Cuboid, and calls Volume and SurfaceArea on it so it can print that info.

In the main function we pass a Cuboid to PrintInfo, which compiles fine. But then we try to pass a Sphere to PrintInfo as well, which results in a compile error. PrintInfo is only set up to accept Cuboid values, even though Sphere values have identical methods.

Let’s get the PrintInfo function to accept both Cuboid and Sphere values. Define a Solid interface consisting of Volume and SurfaceArea methods. Then modify PrintInfo to accept a parameter with a Solid interface type instead of Cuboid.

package main

import (
	"fmt"
	"math"
)

type Cuboid struct {
	width  float64
	length float64
	height float64
}

func (c Cuboid) Volume() float64 {
	return c.length * c.width * c.height
}

func (c Cuboid) SurfaceArea() float64 {
	area := 2 * c.length * c.width
	area += 2 * c.length * c.height
	area += 2 * c.height * c.width
	return area
}

type Sphere struct {
	radius float64
}

func (s Sphere) Volume() float64 {
	return (4.0 / 3.0) * math.Pi * math.Pow(s.radius, 3)
}

func (s Sphere) SurfaceArea() float64 {
	return 2 * math.Pi * math.Pow(s.radius, 2)
}

// YOUR CODE HERE: Define a Solid interface.

// YOUR CODE HERE: Revise PrintInfo to accept a parameter
// that satisfies the Solid interface.
func PrintInfo(c Cuboid) {
	fmt.Println(c)
	fmt.Printf("Volume: %0.3f\n", c.Volume())
	fmt.Printf("Surface Area: %0.3f\n", c.SurfaceArea())
}

func main() {
	c := Cuboid{length: 2.5, width: 5.0, height: 10.5}
	PrintInfo(c)
	s := Sphere{radius: 2.0}
	PrintInfo(s)
}

Compile Error:

./prog.go:47:11: cannot use s (type Sphere) as type Cuboid in argument to PrintInfo

When you’re ready, have a look at our solution.

Exercise 2: Type Assertions

Here are updated Fan and CoffeePot types, both of which satisfy an Appliance interface. We’ve also added a Use method that accepts an Appliance. Currently, Use only calls the TurnOn method on the Appliance

Update Use so that it calls Oscillate on the Appliance if (and only if) it’s a Fan. Use should also call Brew on the Appliance if (and only if) it’s a CoffeePot.

package main

import "fmt"

type Appliance interface {
	TurnOn()
}

type Fan string

func (f Fan) TurnOn() {
	fmt.Println("Spinning")
}
func (f Fan) Oscillate() {
	fmt.Println("Rotating on base")
}

type CoffeePot string

func (c CoffeePot) TurnOn() {
	fmt.Println("Powering up")
}
func (c CoffeePot) Brew() {
	fmt.Println("Heating Up")
}

func Use(appliance Appliance) {
	fmt.Println(appliance)
	appliance.TurnOn()
	// YOUR CODE HERE: If the appliance is a
	// Fan, call its Oscillate method.
	// If the appliance is a CoffeePot, call
	// its Brew method.
}

func main() {
	Use(Fan("Windco Breeze"))
	Use(CoffeePot("LuxBrew"))
}

Desired Output:

Windco Breeze
Spinning
Rotating on base
LuxBrew
Powering up
Heating Up

Here’s our solution.