DEV Community

Ahmet Turkmen
Ahmet Turkmen

Posted on • Originally published at mrturkmen.com on

Go notes (OOP topics) πŸ‡¬πŸ‡§

In this post, I would like to share the notes that I took when I was completing following Go series education on Coursera. I can recommend it for anyone who would like to get rapid introduction to Go programming.

Programming with Google Go

Go course certificate

I should admit that although it looks fancy, nothing can be compared to actual development and contribution to open source projects. The course itself is quite interesting and contains very handy exercises regarding to Go development mentality. I would recommend it for anyone who has some interest in Go development and do not know where to start.

There will be following headers and subheaders which contains some notes on Go Programming language as bulletpoints.

OOP in Go

In Go, there is no such a concept of object oriented programming as in Java, however, it is possible to imply similar approach (object oriented approach) in Go using interfaces and structs.

Classes

There is no β€œClass” keyword in Go.

  • Collection of data fields and functions that share a well-defined responsibility
    • Example: Point class
    • Used in a geometry program
    • Data: x coordinate, y coordinate
    • Funtions:
    • DistToOrigin(), Quadrant()
    • AddXOffSet(), AddYOffset()
    • Classes are template
    • Contain data fields, not data

Classes are supported with structs in Go.


type Point struct {
    X float64
    Y float64
}

Enter fullscreen mode Exit fullscreen mode
  • Structs with methods, structs and methods together allow arbitrary data and functions to be composed.

func (p Point) DistToOrig() {
  t:= math.Pow(p.x,2) + math.Pow(p.y,2) 
  return math.Sqrt(t)
}

func main() {
    p1 := Point(3,4)
    fmt.Println(p1.DistToOrig())
}

Enter fullscreen mode Exit fullscreen mode

Objects

  • Instance of a class
  • Contains real data
  • Example: Point Class

Encapsulation

  • Data can be protected from the programmer
  • Data can be accessed only using methods
  • Maybe we do not trust the programmer to keep data consistent
  • Example: Double distance to origin
    • Option 1: Make method DoubleDist
    • Option 2: Trust programmer to double X and Y directly

Encapsulation is supported as following ;

Create a package called data which has exported function which is PrintX, since it starts with capital letter, in Go, when something starts with capital letter, it means that it is exported

package data 
var x int=1
func PrintX() {
    fmt.Println(x)
}

Enter fullscreen mode Exit fullscreen mode

Main package which is starting point of any application in Go.


package main 
func main() {
    data.PrintX()
}

Enter fullscreen mode Exit fullscreen mode

Controlling access to Structs

  • Hide fields of structs by starting field name with a lower-case letter.
  • Define public methods which access hidden data

package data 

type Point struct{
    x float64
    y float64
}

func (p *Point) InitMe(xn,xy float64) {
    p.x =xn
    p.y =xy
}

func (p *Point) Scale(v float64) {
        p.x=p.x*v
        p.y=p.y*v
}

func (p *Point) PrintMe() { 
    fmt.Println(p.x,p.y)
}

Enter fullscreen mode Exit fullscreen mode

Note that all methods are public ! Since their initial character is capital letter.

package main

func main() {
    var p data.Point 
    p.InitMe(3,4)
    p.Scale(2)
    p.PrintMe()
}

Enter fullscreen mode Exit fullscreen mode
  • Access to hidden fields can only be possible through public access.

Limitation of Method

- Receiver is passed implicitly as an argument to the method

Method cannot modify the data inside the receiver

  • Example: OffsetX() should increase x coordinate
package main

func main(){
    p1:=Point(3,4)
    p1.OffsetX(5)
}

Enter fullscreen mode Exit fullscreen mode

Large Receivers

  • If receiver is large, lots of copying is required

type Image [100] [100]int 
func main() { 
    i1 := GrabImage()
    i1.BlurImage()
}

Enter fullscreen mode Exit fullscreen mode

10.000 ints copied to BlurImage() (Pitfalls)

Pointer Receivers

func (p *Point) OffsetX (v float64) {
    p.x=p.x+v
}

Enter fullscreen mode Exit fullscreen mode
  • Receiver can be a pointer to a type
  • Call by reference, pointer is passed to the method

Point Receivers, Referenceing, Dereferencing

  • No need to dereference
func (p *Point) OffsetX (v int) {
    p.x=p.x+v
}

Enter fullscreen mode Exit fullscreen mode

Point is referenced as p, not *p

No need to reference

package main 

func main() {
    p:=Point{3,4}
    p.OffsetX(5)
    fmt.Println(p.x)
}

Enter fullscreen mode Exit fullscreen mode
  • Do not need to reference when calling the method

Good Programming Practices

  • All methods for type have pointer receivers or
  • All methods for a type have non-pointer receivers
  • Mixing pointer/non-pointer receivers for a type will get confusing !
    • Pointer receiver allows modification

Polymorphism

  • Ability for an object to have different forms depending on the context
  • Example Area() function
    • Rectangle area is base * height
    • Triangle area is 0.5*base*height
  • Identical at a high level of abstraction
  • Different at a low level of abstraction

Inheritance (No Inheritance in GoLang)

  • Sublcass inherits the methods/data of the superclass
  • Example: Speaker superclass
    • Speak() method, pring "<noise> "
  • Subclassses Cat and Dog
    • Also have the Speak() method
  • Cat and Dog are different forms of speaker
  • Remember: Go does not have inheritance

Overriding

  • Subclass redefines a method inherited from the superclass
  • Example: Speaker, Cat, Dog
    • Spekaer Speak() prints β€œ"
    • Cat Speak() prints β€œmeow”
    • Dog Speak() prints β€œwoof”
  • Speak() is polymorphic
    • Different implementations for each class
    • Same signature (name, params and return)

Interface

  • Set of method signatures
    • Name, parameters, return values
    • Implementation is NOT defined
  • Used to express conceptial similarity between types.
  • Example : Shape2D interface
  • All 2D shapes must have Area() and Perimeter()

Satisfying an Interface

  • Type satisfies an interface if type defines all methods specified in the interface.
    • Same method signatures
  • Rectangle and Triangle types satisfy the Shape2D interface
    • Must have Area() and Perimeter() methods
    • Additional methods are OK.
  • Similar to inheritance with overriding.

Example


type Shape2D interface {
        Area() float64
        Perimeter() float64
}
type Triangle {...}

func (t Triangle) Area() float64 {....}
func (t Triangle) Perimeter() float64 {....}

Enter fullscreen mode Exit fullscreen mode
  • Triangle type satisfies the Shape2D interface
  • No need to state it explicitly

Interface vs Concrete Types

Concrete Types

  • Specify the exact representation of the data and methods
  • Complete method implementation is included ### Interface Types
  • Specifies some method signatures
  • Implementations are abstracted

Interface Values

  • Can be treated like other values
    • Assigned to variables
    • Passed, returned
  • Interface values have two components
  1. Dynamic Type : Concrete type which it is assigned to
  2. Dynamic Value: Value of the dynamic type

Defining an interface type


type Speaker interface { Speak()}
type Dog struct {name string }
func (d Dog) Speak() {
    fmt.Println(d.name)
}
func main() {
    var s1 Speaker
    var d1 Dog{"Brian"}
    s1=d1
    s1.Speak()

Enter fullscreen mode Exit fullscreen mode

Dynamic type is Dog and dynamic value is d1.

An interface can have a nil dynamic value

    var s1 Speaker 
    var d1 *Dog 
    s1=d1

Enter fullscreen mode Exit fullscreen mode
  • d1 has no concrete value yet
  • s1 has a dynamic type but no dynamic value

Nil Dynamic Value

  • Can still call the Speak() method of s1
  • Does not need a dynamic value to call
  • Need to check inside the method

func (d *Dog)Speak() { 
    if d==nil{
        fmt.Println("<noise>")
    }else{
        fmt.Println(d.name)
    }   
}

var s1 Speaker
var d1 *Dog 
s1=d1
s1.Speak() // it works, since s1 is mapped to d1 

Enter fullscreen mode Exit fullscreen mode

Nil Interface Value

  • Interface with nil dynamic type
  • Very different from an interface with a nill dynamic value

Nil dynamic value and valid dynamic type

var s1 Speaker 
var d1 *Dog 
s1=d1 

Enter fullscreen mode Exit fullscreen mode
  • Can call a method since type is known, Nil dynamic type
var s1 Speaker *(there is no actually method to call)

Enter fullscreen mode Exit fullscreen mode
  • Cannot call a method, runtime error

No dynamic type and no dynamic value then you cannot call the interface…

Using Interfaces

  • Need a function which takes multiple types of parameter
  • Function foo() parameter
    • Type X or Type Y
  • Define interface Z
  • foo() parameter is interface Z
  • Types X and Y satisfy Z
  • Interface methods must be those needed by foo()

Example Interface for Shapes

Pool in a Yard

  • I need to put a pool in my yard
  • Pool need to fit in my yard
    • Total area must be limited
  • Pool needs to be fenced
    • Total perimeters must be limited
  • Need to determine if a pool shape satisfies criteria
  • FitInYard()
    • Takes a shape as argument
    • Returns true if the shape satisfies criteria
  • FitInYard()
    • Many Possible shape types
    • Rectangle, triangle, circle
  • FitInYards() should take many shape types
  • Valid shape types must have
    • Area()
    • Perimeter()
  • Any shape with these methods is OK.

type Shape2D interface {
    Area() float64
    Perimeter() float64
}
type Triangle {...}
func (t Triangle) Area() float64 {...}
func (t Triangle) Perimeter() float64 {...}
type Rectangle {...}
func(t Rectangle) Area() float64 {...}
func (t Rectangle) Perimeter() float64 {....}

Enter fullscreen mode Exit fullscreen mode

Rectangle and Triangle satisfy Shape2D interface.

FitInYard() Implementation


func FitInYard(s Shape2D) bool {
    if (s.Area() > 100 && s.Perimeter() > 100) {
        return true
    }
return false
} 

Enter fullscreen mode Exit fullscreen mode

Empty Interface

  • Empty interface specifies no methods
  • All types satisfy the empty interface
  • Use it to have a function accept any type as a parameter
func PrintMe(val interface{} ) {
    fmt.Println(val)
} 

Enter fullscreen mode Exit fullscreen mode

Type Assertions

Concealing Type Differences__

  • Interfaces hide the differences between types
func fitInYard(s Shape2D)bool {
    if (s.Area() >100 && s.Perimeter()>100){
        return true
    }
    return false
}

Enter fullscreen mode Exit fullscreen mode
  • Sometimes you need to treat different types in different ways

Exposing Type Differences

- Example: Graphics program

DrawShape() will draw any shape

func DrawShape (s Shape2D) {..... }

Enter fullscreen mode Exit fullscreen mode

Underlying API has different drawing functions for each shape

func DrawRect (r Rectangle) {....
func DrawTriangle(t Triangle) {...

Enter fullscreen mode Exit fullscreen mode
  • Concrete type of shape s must be determined

Type Assertions for Disambiguation

  • Type assertions can be used to determine and extract the underlying concrete type
func DrawShape(s Shape2D) bool {
    rect,ok :=s.(Rectangle)
    if ok {
        DrawRect(rect)
    }
    tri,ok := s.(Triangle)
    if ok {
        DrawRect(tri)
    }
}

Enter fullscreen mode Exit fullscreen mode
  • Type assertion extracts Rectangle from Shape2D
    • Concrete type in parentheses
  • If interface contains concrete type
    • rect == concrete type, ok == true
  • If interface does not contain concrete type
    • rect==zero, ok==false

Type Switch

  • Switch statement used with a type assertion
func DrawShape(s Shape2D) bool {

    switch:= sh:=s.(type) {
    case Rectangle: 
        DrawRect(sh)
    case Triangle: 
        DrawTri(sh) 
    }
}

Enter fullscreen mode Exit fullscreen mode

Error Interface

  • Many Go programs return error interface objects to indicate errors
type error interface {
    Error() string
}

Enter fullscreen mode Exit fullscreen mode
  • Correct operation : error==nil
  • Incorrect operation: Error() print error message

Handling Errors

f,err := os.Open("/harris/text.txt")
if err!=nil {
    fmt.Println(err)
    return 
}

Enter fullscreen mode Exit fullscreen mode
  • Check whether the error is nil
  • If it is not nil, handle it
  • fmt package calls the error() method to generate string to print

Keep in mind that the topics which are mentioned on this post is just brief summary, it means that all subheaders and headers can be extended to any size, however these are just bulletpoints and overall information in Functions, Methods, and Interfaces in Go module of specialization serie.

In next post, notes which are taken from concurrency module of the specialization serie will be posted.

Take care ! πŸ‘‹πŸ»

Top comments (0)