Go Programming - OOP

Concepts of Programming Languages

Sebastian Macke

Rosenheim Technical University

Last lecture

2

Last Exercise

3

Error Handling

4

Errors in Go

Go doesn't have try-catch exception based error handling.
Go distinguishes between recoverable and unrecoverable errors.

Recoverable: e. g. file not found

err := ioutil.WriteFile(src.Name(), []byte("hello"), 0644)
if err != nil {
    log.Error(err)
}

Unrecoverable: array access outside its boundaries, out of memory

5

Go defer: run code before function exists

The "defer" statement lets us ensure that code runs before a function exits

f, err := os.Open("myfile.txt")
if err != nil {
    return err
}
defer f.Close() // Will be executed on function exit, even in case of an unrecoverable error.
....
// read, write
6

unrecoverable error: panic and recover by using defer

func getElement(array []int, index int) int {
    if index >= len(array) {
        panic("Out of bounds")
    }
    return array[index]
}

func getElementWithRecover(array []int, index int) (value int) {
    defer func() {
        r := recover()
        if r != nil {
            fmt.Println("Recovered with message '", r, "'")
        }
        value = -1
    }()
    return getElement(array, index)
}

func main() {
    array := []int{3, 4, 5}
    ret := getElementWithRecover(array, 3)
    fmt.Println("return value: ", ret)
}
7

Exception Pro and Cons

8

Object Oriented Programming

9

Structure of object oriented programming

Wikipedia: Object-oriented programming (OOP) is a programming paradigm based on the concept of "objects", which can contain data and code: data in the form of fields (often known as attributes or properties), and code, in the form of procedures (often known as methods).

10

Principles of Object Oriented Programming

Go is not a pure object oriented programming language but
allows an object-oriented style of programming

11

Encapsulation I: No classes, but structs

// Rational represents a rational number numerator/denominator.
type Rational struct {
    numerator   int
    denominator int
}

// Constructor
func NewRational(numerator int, denominator int) Rational {
    if denominator == 0 {
        panic("division by zero")
    }
    return Rational{
        numerator:   numerator,
        denominator: denominator,
    }
}
12

Encapsulation II: Bind together code and data it manipulates

// Multiply method for rational numbers
func Multiply(r Rational, y Rational) Rational {
    return NewRational(r.numerator*y.numerator, r.denominator*y.denominator)
}
// Multiply method for rational numbers
func (r *Rational) Multiply(y Rational) Rational {
    return NewRational(r.numerator*y.numerator, r.denominator*y.denominator)
}

r1 := NewRational(1, 2)
r2 := NewRational(2, 4)
r3 := r1.Multiply(r2)

The variable r is in both cases similar to the Java this, which reference to the class instance

13

Orthogonal: Encapsulation can be done with any type

package main

import "fmt"

type MyInt int

func (b *MyInt) Inc() {
    *b = *b + 1
}

func main() {
    var b MyInt = 10
    fmt.Println(b)
    b.Inc()
    fmt.Println(b)
}
14

Syntax level OOP

package main

import "fmt"

type Bar struct{}

func (b *Bar) GetHello() string {
    fmt.Println(b)
    return "Hello"
}

type Foo struct {
    B *Bar
}

func main() {
    var f Foo
    fmt.Println(f.B)            // Output "nil"
    fmt.Println(f.B.GetHello()) // Null Pointer error or "Hello"?
}
15

Syntax level OOP

Encapsulation of methods is basically is just a syntax element.

func (r *Rational) Multiply(y Rational) Rational {
 ....
}

is internally transformed into

func Multiply(r *Rational, y Rational) Rational {
 ....
}

A lot of languages do it this way.

16

Syntax level OOP

17

Composition VS. Inheritance

Composition and inheritance are two ways to achieve code reuse and design modular systems

class Engine {
    ....
}
class Car {
    private Engine engine
}
class Animal {
    ....
}

class Dog extends Animal {
}
18

Composition VS. Inheritance?

Composition and inheritance are two ways to achieve code reuse and design modular systems

19

The issue with inheritance

20

Embedding

// Point is a two dimensional point in a cartesian coordinate system.
type Point struct{ x, y int }
// ColorPoint extends Point by adding a color field.
type ColorPoint struct {
    Point // Embedding simulates inheritance but it is (sort-of) delegation!
    c     int
}
    fmt.Println(cp.x)       // access inherited field
21

Delegation of Functions in Go

comparison of behavior in Java and Go at
- ../src/oop/delegation/delegation.go
- ../src/oop/delegation/delegation.java
- ../src/oop/delegation2/delegation.go
- ../src/oop/delegation2/delegation.java

22

Polymorphism I

An interface is a set of methods

In Java:

interface Switch {
    void open();
    void close();
}

In Go:

type OpenCloser interface {
    Open()
    Close()
}
23

Polymorphism II

class Door implements Switch {
  public void Open() { ... }
  public void Close() { ... }
}
type Door struct {}
func (d *Door) Open() { ... }
func (d *Door) Close() { ... }

Door implicitly satisfies the interface OpenCloser

Go supports polymorphism only via interfaces, not through classes

24

Polymorphism III: Example The stringer interface

The print functions in the fmt package support the following interface

type Stringer interface {
    String() string
}
if tmp, ok := object.(Stringer); ok {
    // The object implements stringer
}
25

Polymorphism III: The stringer interface

package main

import "fmt"

type DoorOpen bool

func (d DoorOpen) String() string {
    if d == true {
        return "Door is open"
    } else {
        return "Door is closed"
    }
}

func main() {
    var d DoorOpen = false
    fmt.Println(d)
}
26

Interfaces and Polymorphism

func main() {
    var p = Point{1, 2}
    var cp = ColorPoint{Point{1, 2}, 3} // embeds Point
    fmt.Println(p)
    fmt.Println(cp)
    fmt.Println(cp.x) // access inherited field

    // p = cp       // does not work: No hierarchy, no polymorphism
    // p = cp.Point // works

    // s is an interface and supports Polymorphism
    var s fmt.Stringer
    s = p // check at compile time
    fmt.Println(s)
    s = cp
    fmt.Println(s)
}
27

Recap: Go does support a dynamic type

func PrintVariableDetails(v any) {
    typeof := reflect.TypeOf(v)
    fmt.Printf("The variable with type '%s' has the value '%v'\n", typeof.Name(), v)
}

func main() {
    var someValue any
    someValue = 2
    PrintVariableDetails(someValue)

    someValue = "abcd"
    PrintVariableDetails(someValue)

    if tmp, ok := someValue.(string); ok {
        fmt.Println("someValue is a string and has the value", tmp)
    }
}
type any = interface{}
28

Summary

29

Exercise 3

30

Exercise

31

Questions

32

Multiple embeddings of same variable or function names

type Foo struct {
    Name string
}

type Bar struct {
    Name string
}

type X struct {
    Foo
    Bar
}

func main() {
    y := X{
        Foo: Foo{Name: "Foo!"},
        Bar: Bar{Name: "Bar!"},
    }
    fmt.Print(y.Foo.Name)
    fmt.Print(y.Bar.Name)
    //fmt.Print(y.Name) // compile error, Ambiguous Reference
}
33

Thank you

Sebastian Macke

Rosenheim Technical University

Use the left and right arrow keys or click the left and right edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)