Object Oriented Programming

CIS 193 – Go Programming

Prakhar Bhandari, Adel Qalieh

CIS 193

Course Logistics

Notes From Homework 0

Remember to run gofmt!
Test your code!

Helpful Tools
- go vet
- golint

Structs

Aggregate data type that groups zero or more named values of arbitrary types

type Person struct {
    ID     int
    Name   string
    Age    int
}

var jim Person
jim.Age = 34

Field order matters
Fields are only exported if uppercase first letter

Struct Literals

type Point struct {
    X, Y int
}

Values in order - every field must be included

p1 := Point{1, 2}

Field names and values

p2 := Point{X: 2} // Y = 0

Cannot use both forms in the same literal

If each field is comparable, structs can be compared using the == operator

Struct Embedding

Instead of

type Circle struct {
    X, Y, Radius int
}

We can do

type Point struct {
    X, Y int
}

type Circle struct {
    Center Point
    Radius int
}

Why would we want to do this?

Struct Embedding

Accessing fields

var c Circle
c.Center.X = 3
c.Center.Y = 4
w.Radius = 6

This is messy - what would be a better way?

Struct Embedding and Anonymous Fields

type Point struct {
    X, Y int
}

type Circle struct {
    Point
    Radius int
}

Accessing fields

var c Circle
c.X = 3
c.Y = 4
w.Radius = 6

Point is embedded in Circle

Struct Embedding and Anonymous Fields

How do we initialize structs with anonymous embedded fields?

c := Circle{X: 4, Y: 5, Radius: 8} // compile error

We have two options

c := Circle{Point{8, 8}, 5}
c := Circle{
    Point:  Point{X: 8, Y: 8},
    Radius: 5, // this comma is necessary
}

Recursive Structs

Struct type T can't have field T

How can we make recursive types?

Pointers

A variable is a piece of storage that contains a value

Pointers are the address of a variable, the location where a value is stored

Pointer Syntax

Pointers allow us to read/update the value of a variable indirectly

var x int
&x // the "address of x", has type *int

&x is a pointer to an int

func incr(p *int) int {
    *p++  // doesn't change p
    return *p
}

x := 1
incr(&x)              // x is now 2
fmt.Println(incr(&x)) // "3" (and x is 3)

*p is the variable to which p points to

Pointers with Structs

type Person struct {
    ID     int
    Name   string
    Age    int
}

var p Person
name := &p.Name
*name = "Bob"

var ptr *Person = &p
ptr.Name += " LastName"

Recursive Structs

type Tree struct {
    value       int
    left, right *Tree
}

Methods

Declared as a variant of the ordinary function declaration, the extra type appears before the function name

type Point struct {
    X, Y float64
}

func (p Point) Distance(q Point) float64 {
    return math.Hypot(q.X-p.X, q.Y-p.Y)
}

p := Point{1,1}
q := Point{2,2}
fmt.Println(p.Distance(q)) // 1.4142135623730951

p is the method's receiver

Go typically uses short receiver names, eg: first letter of the type name

Methods with a Pointer Receiver

Why is it useful to use pointers as receivers?

func (p *Point) Scale(factor float64) {
    p.X *= factor
    p.Y *= factor
}

Style: If one method for a type has a pointer receiver, then all should

Receivers can only be named types and pointers to named types

(Point) // ok
(*Point) // ok

type P *int
(P) // not ok

Receivers

Using a pointer receiver

p1 := &Point{1, 1}
p1.Scale(4)
fmt.Println(*p1) // {4, 4}

What happens here?

p1 := Point{1, 1}
p1.Scale(4)
fmt.Println(p)

Go does an implicit *p or &p conversion on variables

String()

Go has a String() method to help with printing

func (c Celsius) String() string {
    return fmt.Sprintf("%g°C", c)
}

Works with:

fmt.Println(c)
fmt.Println(c.String())
fmt.Printf("%v", c)
fmt.Printf("%s", c)

Composition by Struct Embedding

type Point struct {
    X, Y float64
}

type Circle struct {
    Point
    Radius int
}

Any Circle can access the methods of Point

var c Circle
c.X = 2
c.Y = 4
c.Scale(4)
fmt.Printf("%+v\n", c) // {Point:{X:8 Y:16} Radius:0}

The methods of Point are promoted to Circle

How do we handle encapsulation?

Method Values and Expressions

Method value: binds a method to a receiver value

distanceFromP := p.Distance
fmt.Println(distanceFromP(q))

Method Expression

distance := Point.Distance
fmt.Println(distance(p, q))

Basic Testing

Use go test

Applies to files that end in "_test.go"

Test function syntax

import "testing"

func TestName(t *testing.T) {
    //
}

Must begin with "Test", the name of the rest of the function must begin with a capital letter

Testing examples

func TestPalindromeSimple(t *testing.T) {
    if !IsPalindrome("racecar") {
        t.Error(`IsPalindrome("racecar") = false`)
    }
}

func TestIsPalindrome(t *testing.T) {
    var tests = []struct {
        input string
        want  bool
    }{
        {"", true},
        {"a", true},
        {"aa", true},
        {"ab", false},
    }
    for _, test := range tests {
        if got := IsPalindrome(test.input); got != test.want {
            t.Errorf("IsPalindrome(%q) = %v", test.input, got)
        }
    }
}

Homework 2

Thank you

Prakhar Bhandari, Adel Qalieh

CIS 193