// 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