types2.goΒΆ

// types2.go

package main

import (
    "fmt"
    "math"
    "strings"
)

///////////////////////////////////////////////////////////////////////////////////
//
// Point
//
///////////////////////////////////////////////////////////////////////////////////

type Point struct {
    x, y float64
}

// This is like a constructor: it creates a new object of type Point.q
func makeOrigin() Point {
    return Point{0.0, 0.0}
}

// This is a method: p is called the method's receiver. It's like the "this"
// pointer in C++ or Java.
//
// Any method with the exact name String() string will then be called by print
// functions in fmt.
func (p Point) String() string {
    return fmt.Sprintf("(%v, %v)", p.x, p.y)
}

// This is an ordinary function: it is not a method.
// It's called like dist(p, q).
func dist(a, b Point) float64 {
    dx := a.x - b.x
    dy := a.y - b.y
    return math.Sqrt(dx*dx + dy*dy)
}

// This is a method version of dist.
// It's called like p.distTo(q).
func (p Point) distTo(other Point) float64 {
    return dist(p, other)
}

///////////////////////////////////////////////////////////////////////////////////
//
// Color
//
///////////////////////////////////////////////////////////////////////////////////

// 0 <= uint8 <= 255
type Color struct {
    r, g, b uint8
}

// This is a method: c is the receiver.
func (c Color) String() string {
    return fmt.Sprintf("RGB(%v, %v, %v)", c.r, c.g, c.b)
}

// A (global) map of names and their colors.
var colorName map[string]Color = map[string]Color{
    "red": Color{255, 0, 0}, "green": Color{0, 255, 0}, "blue": Color{0, 0, 255},
}

// Returns the color of the given name.
// If name is not a known color, black and false is returned.
// If name is known, the associated color and true is returned.
func makeColor(name string) (Color, bool) {
    n := strings.TrimSpace(name)
    n = strings.ToLower(n)
    c, ok := colorName[n]
    if ok {
        return c, true
    } else {
        return Color{0, 0, 0}, false
    }
} // makeColor

// Creates and returns a new grayscale color.
func makeGray(n uint8) Color {
    return Color{n, n, n}
}

// This is a method, with receiver c of type Color.
func (c Color) brightness() float64 {
    return float64(c.r+c.g+c.b) / 255.0
}

// The type of c is *Color (instead of just Color) because invert() modifies
// c. If there was no * and c were just of type Color, then c would be passed
// by a value, and invert() would modify that copy --- which is useless.
//
// Also note that we even though c is of type *Color (i.e. pointer to Color),
// we still use "." to access the values in it. In C/C++, you would use -> in
// this case.
func (c *Color) invert() {
    c.r, c.g, c.b = 255-c.r, 255-c.g, 255-c.b
}

//
// Types in Go have an associated **method set**.
//
// For the type Color, the associated method set is all the methods that have
// a Color as a receiver, i.e. brightness and String in this example.
//
// For the type *Color, the associated method set is all the methods that have
// a *Color as a receiver, or Color as a receiver, i.e. brightness, String, and
// invert in this example.
//
// In general, the method set of a type T are all the methods have T as a
// receiver. The method set of a type *T are all the methods that have either
// *T or T as a receiver.
//
// Method sets are important when we get to interfaces: a type implements an
// interface if its method set is a superset of the methods listed in the
// interface.
//

///////////////////////////////////////////////////////////////////////////////////
//
// Colored point
//
///////////////////////////////////////////////////////////////////////////////////

// Go does *not* have type inheritance as found in object-oriented languages
// like C++, Java, or Python. Instead, it allows you to **embed** a struct
// within another struct.
//
// For a good discussion of embedding, see this section of the Effective Go:
// https://golang.org/doc/effective_go.html#embedding
type ColoredPoint struct {
    Color
    Point
}

///////////////////////////////////////////////////////////////////////////////////
//
// test code
//
///////////////////////////////////////////////////////////////////////////////////

func pointTest() {
    p := Point{2, -3}
    fmt.Println(p)
    fmt.Println(dist(Point{0, 0}, Point{1, 1}))
}

func colorTest() {
    c, _ := makeColor(" Red  ")
    fmt.Println(c)
    c.invert()
    fmt.Println(c)

    g := makeGray(144)
    fmt.Println(g)
    g.invert()
    fmt.Println(g)
}

func coloredPointTest() {
    red, _ := makeColor("red")
    cp := ColoredPoint{red, Point{2, 3}}
    fmt.Println(cp)
    cp.x = 4
    fmt.Println(cp)

    // Note that invert is in the method set for *Color, and not ColoredPoint
    // or *ColoredPoint. By embedding Color in ColoredPoint, Go lets us call
    // its methods through cp.
    cp.invert()
    fmt.Println(cp)
    fmt.Println(cp.distTo(makeOrigin()))
    // this next line causes a compiler error since cp is of type ColorPoint,
    // but dist expects a two values of type Point
    // fmt.Println(dist(cp, makeOrigin()))
}

func main() {
    pointTest()
    colorTest()
    coloredPointTest()
} // main