DEV Community

nadirbasalamah
nadirbasalamah

Posted on

Golang tutorial - 7 struct and interface

OOP in Go

Object oriented programming (OOP) is a common pattern in many programming languages. Object oriented programming has many advantages like reusability, polymorphism and encapsulation. The concept of OOP can be implemented using Go but with different approach. Basically, some concepts in OOP like class, interface and methods are supported in Go but there is no type hierarchy. In Golang official documentation in FAQ section there is an interesting answer from a question "Is Go an object-oriented language?" that you can check here

Pointer

Pointer is an addressing mechanism that supported in Go. In pointer, there are two operators called addressing or pointing into certain variable (&) and dereference (*).

Let's take a look at this example:

func main() {
    a := 42
    //prints out the value of a
    fmt.Println("value of a: ", a)

    //prints out the memory address that pointing to "a" variable
    fmt.Println("memory address of a: ", &a)
}
Enter fullscreen mode Exit fullscreen mode

Output:

value of a:  42
memory address of a:  0xc000014080
Enter fullscreen mode Exit fullscreen mode

Based on that code, & operator is used to addressing into certain variable.

With pointer, a variable is capable to addressing into another variable. For example, a b variable can addressing into another variable (in this code the variable is called a). Here it is the code example based on that case.

func main() {
    a := 42
    //prints out the value of a
    fmt.Println("value of a: ", a)

    //prints out the memory address that pointing to "a" variable
    fmt.Println("memory address of a: ", &a)

    b := &a
    //prints out the memory address of "b" variable
    fmt.Println("value of b:", b)

    //to prints out the value of b, use *  (dereference) operator
    fmt.Println("value of b using * operator: ", *b)
}
Enter fullscreen mode Exit fullscreen mode

Output:

value of a:  42
memory address of a:  0xc000014080
value of b: 0xc000014080
value of b using * operator:  42
Enter fullscreen mode Exit fullscreen mode

Notice that the value of b and value of a are equals because the b variable contains the memory address that same with a variable.

The illustration looks like this.
Pointer illustration

If the value of a changes, the value of b also changes as well.

//changes the value of "a"
a = 45
fmt.Println("value of a: ", a)
fmt.Println("value of b: ", *b)
Enter fullscreen mode Exit fullscreen mode

Output:

value of a:  45
value of b:  45
Enter fullscreen mode Exit fullscreen mode

struct

Class is a concept that mainly used in OOP. In Go, there is a similiar concept in class that called struct. To declare a struct can be done by using this syntax:

type structName struct {
    //field name type
    member int
    member2 string
    member3 []string
}
Enter fullscreen mode Exit fullscreen mode

Here it is an example of using struct. This struct illustrates a person that has a behavior to say something.

//declare a struct called person
type person struct {
    name string
    age  int
}

//declare a method say() with type person as receiver
func (p person) say() {
    fmt.Println("Hello, my name is: ", p.name)
}
Enter fullscreen mode Exit fullscreen mode

To use the defined struct, assign defined struct into variable like this:

//instantiate the person
p1 := person{name: "Enki Gilbert", age: 42}
//call a method say()
p1.say()
Enter fullscreen mode Exit fullscreen mode

Here it is the complete code:

package main

import "fmt"

//declare a struct called person
type person struct {
    name string
    age  int
}

//declare a method say() with type person as receiver
func (p person) say() {
    fmt.Println("Hello, my name is: ", p.name)
}

func main() {
    //instantiate the person
    p1 := person{name: "Enki Gilbert", age: 42}
    //call a method say()
    p1.say()

}
Enter fullscreen mode Exit fullscreen mode

Output:

Hello, my name is:  Enki Gilbert
Enter fullscreen mode Exit fullscreen mode

The alternative syntax of using defined struct can be done by ignoring the field name.

//instantiate the person
p1 := person{"Enki Gilbert", 42}
Enter fullscreen mode Exit fullscreen mode

The anonymous struct is also available in Go.

s1 := struct{
    //declare some fields
    field1 int
    field2 []string
}{
    //instantiate directly
    field1: 12,
    field2: []string{"hi","mate"},
}
Enter fullscreen mode Exit fullscreen mode

Composition

The inheritance feature isn't supported in Go, we can use composition mechanism instead. Here it is the example of struct composition:

//declare a struct called person
type person struct {
    name string
    age  int
}

//composition example
//declare a struct called manager that includes another struct called person
type manager struct {
    person
    team string
}

//declare a method say() with type person as receiver
func (p person) say() {
    fmt.Println("Hello, my name is: ", p.name)
}

func main() {
    //instantiate the person
    p1 := person{"Enki Gilbert", 42}

    //instantiate the manager and assign person field by using p1 variable
    m1 := manager{person: p1, team: "Racing Team Solvalou"}

    //the say() method is still available
    m1.say()

}
Enter fullscreen mode Exit fullscreen mode

Output:

Hello, my name is:  Enki Gilbert
Enter fullscreen mode Exit fullscreen mode

Based on that code, we declare a struct called manager that compose another struct called person. The say() method is still available to manager struct because it composes another struct (person) that has a say() method.

The alternative syntax of instatiate the struct that has a struct composition:

//instatiate the person struct into person field directly
m1 := manager{person: person{"Enki Gilbert", 42}, team: "Racing Team Solvalou"}
Enter fullscreen mode Exit fullscreen mode

Interface

Interface is a powerful concept in programming. Interface is similiar with struct but it only contains some abstract methods. In Go, interface defines an generic abstraction of behaviors. To declare an interface, can be done by using this syntax:

//type name interface
type name interface {
    //declare some methods
    method1()
    method2() int
}
Enter fullscreen mode Exit fullscreen mode

Here it is the example of using interface.

//declare a rectangle struct
type rectangle struct {
    length int
    width  int
}

//declare an interface with area() as a member
type shape interface {
    area() int
}

//declare a method area()
//the rectangle struct implements area() method in shape interface
func (r rectangle) area() int {
    return r.length * r.width
}

//declare a method with type shape as a parameter
func info(s shape) {
    fmt.Println("the area: ", s.area())
}

func main() {
    r1 := rectangle{12, 12}
    //r1 is a rectangle type. rectangle implements all methods in shape interface.
    info(r1)
}
Enter fullscreen mode Exit fullscreen mode

Output:

the area:  144
Enter fullscreen mode Exit fullscreen mode

Based on that example, we declare a struct called rectangle and an interface called shape. The rectangle implements a method called area() in shape interface. There is a method called info() with shape type as a parameter. The info() method's parameter is accessible for every struct that implements all methods in shape interface.

If there is another struct, in this case called square. The info() method is also available because square struct is also implements all methods in shape interface.

type square struct {
    side int
}
//the square struct implements area() method in shape interface
func (s square) area() int {
    return s.side * s.side
}

Enter fullscreen mode Exit fullscreen mode

Here it is the complete example:

package main

import (
    "fmt"
)

//declare a rectangle struct
type rectangle struct {
    length int
    width  int
}

//declare a square struct
type square struct {
    side int
}

//declare an interface with area() as a member
type shape interface {
    area() int
}

//declare a method area()
//the rectangle struct implements area() method in shape interface
func (r rectangle) area() int {
    return r.length * r.width
}

//the square struct implements area() method in shape interface
func (s square) area() int {
    return s.side * s.side
}

//declare a method with type shape as a parameter
/**
anything that implements all methods in shape interface is considered as a shape in general.
for this case the rectangle and square is a shape because implements all methods in shape interface
**/
func info(s shape) {
    fmt.Println("the area: ", s.area())
}

func main() {
    r1 := rectangle{12, 12}
    info(r1)

    s1 := square{25}
    info(s1)
}
Enter fullscreen mode Exit fullscreen mode

Output:

the area:  144
the area:  625
Enter fullscreen mode Exit fullscreen mode

The composition of interface is also available. The example can be checked in io package here.

Method sets

Method set is a function that associates with certain type. For example the area() is a function that associates with shape type. If the receiver of method set is a value, then the method can be used by a value or a pointer of a value.
For example, this area() method is available for value and pointer of type shape (in this example is a rectangle).

//shape interface
type shape interface {
    area() int
}
func (r rectangle) area() int {
    return r.length * r.width
}
//used in info() function
func info(s shape) {
    fmt.Println("the area: ", s.area())
}
func main() {
    r1 := rectangle{12, 12}
    //with value
    info(r1)
    //with pointer
    info(&r1)
}
Enter fullscreen mode Exit fullscreen mode

Output:

the area:  144
the area:  144
Enter fullscreen mode Exit fullscreen mode

If the receiver of method set is a pointer, then the method is only available with pointer.

Example:

//the receiver is a pointer
func (r *rectangle) area() int {
    return r.length * r.width
}

func info(s shape) {
    fmt.Println("the area: ", s.area())
}

func main() {
    r1 := rectangle{12, 12}
    //with value, throw an error
    // info(r1)
    //with pointer
    info(&r1)
}
Enter fullscreen mode Exit fullscreen mode

Output:

the area:  144
Enter fullscreen mode Exit fullscreen mode

Notes

  • Learn more about struct in here
  • Learn more about pointer in here
  • Learn more about method sets in here

I hope this article helpful for helping to learn the Go programming language. If you have any thoughts or feedbacks, you can write it in the discussion section below.

Discussion (0)