DEV Community

Cover image for How to use Object Oriented Programming in Go
Chandler for Casual Coders

Posted on

How to use Object Oriented Programming in Go

Go is not an Object Oriented Programming language. It is definitely aimed more at the functional crowd, as its support for OOP is lacking at best, preferring C-style structs that can have methods attached to the more traditional style of OOP that Java, Javascript, Python, and C++ support.

How would I get started?

To create a class in Go, you can use the type keyword, followed by the name of the class, and then the struct that represents the data for the class. Here is an example of a simple class called Person that has two fields, name and age:

type Person struct {
  Name string
  Age int
}
Enter fullscreen mode Exit fullscreen mode

You can then create an instance of this class by using the new keyword, like this:

p := new(Person)
Enter fullscreen mode Exit fullscreen mode

To set the values of the fields in the class, you can use the dot notation, like this:

p.Name = "John"
p.Age = 20
Enter fullscreen mode Exit fullscreen mode

You can also create a class with methods, which are functions that are associated with a specific class. Here is an example of a Person class with a greet method:

type Person struct {
  name string
  age int
}

func (p *Person) Greet() string {
  return "Hello, my name is " + p.Name
}

Enter fullscreen mode Exit fullscreen mode

You can call the greet method on an instance of the Person class like this:

p := new(Person)
p.Name = "John"
p.Age = 20

fmt.Println(p.Greet())  // Output: "Hello, my name is John"
Enter fullscreen mode Exit fullscreen mode

Just like when writing other types of functions in Go, capital letters signify public functions that can be called externally. The same goes for the attributes of the struct.

Constructor? I barely know her!

There is no concept of a constructor method in Go.

I like defining defaults as much as the next OOP programmer, and the easiest (and only) way to do it in Go is using a factory method that returns a new instance of said "class" (struct).

type Person struct {
  Name string
  Age int
}

func NewPerson(name string, age int) *Person {
  p := new(Person)
  p.Name = name
  p.Age = age
  return p
}
Enter fullscreen mode Exit fullscreen mode

What about my inheritance?

In Go, class inheritance works by allowing type embedding. Type embedding allows you to use one type as a part of another type. This is similar to class inheritance in other languages, but instead of subclassing one type from another, a type is embedded inside another type. This allows the embedded type to inherit the properties and methods of the containing type. Type embedding is a powerful technique that can be used to create complex types with a lot of functionality.

Let's say we have a type "Vehicle" and a type "Car" which embeds Vehicle. Car will inherit all the properties and methods of Vehicle, as well as any additional ones it may have. So, for example, if Vehicle has a "Drive()" method, Car will also have this method. This allows us to reuse code and create complex types.

package main

import "fmt"

type Vehicle struct {
    Wheels int
}

func (v *Vehicle) Drive() {
    fmt.Println("Driving")
}

type Car struct {
    Vehicle
    Doors int
}

func main() {
    c := Car{}
    c.Drive()
}
Enter fullscreen mode Exit fullscreen mode

You can override the Drive() function by making a new one but instead of passing in a pointer to a vehicle, you do a pointer to a Car.

func (c *Car) Drive() {
    fmt.Println("Driving on a road")
}
Enter fullscreen mode Exit fullscreen mode

This is not the same for attributes (in this case Doors for the Car and Wheels for both). If we want to set the number of wheels on the Car, we will need to create an instance of Vehicle in the Car. Here is an example factory function that does just that.


func NewCar(doors int) *Car {
    c := Car{
       Vehicle{Wheels: 4},
       Doors: doors,
    }
    return &c
}

// and if we want to access the Wheels on the Car
mustang := NewCar(4)
fmt.Println("A car has ", mustang.Vehicle.Wheels)
Enter fullscreen mode Exit fullscreen mode

One more thing of note, pointers allow more control with the structs than using by value, so always try to use a pointer when working with classes in Golang.

I hope this tutorial helped. Question? Comments? Concerns? Leave them below!

Oldest comments (3)

Collapse
 
c4r4x35 profile image
Srinivas Kandukuri

easy to read and nice examples. Good Job @casualcoder

Collapse
 
rostislaved profile image
Rostislav

You would be surprised that the absence of inheritance doesn't imply absence of ability to follow OO paradigm.
And please learn the difference between association and generalization.

Collapse
 
enginy88 profile image
Engin Yüce

Hi! I have some comments for your post, especially under inheritance section:

1) In the last snippet you should create a new instance of Car as:

c := Car{
   Vehicle: Vehicle{Wheels: 4},
   Doors: doors,
}
Enter fullscreen mode Exit fullscreen mode

Otherwise you will get mixture of field:value and value elements in struct literal error on compile time.

2) The next thing that irritates me (again in the last snippet) is accessing the wheel count of car object like mustang.Vehicle.Wheels. Even though it's fine to call like that, since Vehicle struct composited as anonymous struct inside Car struct, it's better to call as mustang.Wheels. Otherwise there no meaning to use anonymous definition.

Or if we want to keep using mustang.Vehicle.Wheels, it's better to define Car struct as for readability purposes:

type Car struct {
    Vehicle Vehicle
    Doors int
}
Enter fullscreen mode Exit fullscreen mode

Thanks for the post and take care!