OOP and Go… Sorta

So I started Go July 18th, 2016. Three weeks later, I gave my first tech talk ever at the NYC Women Who Go meetup. This is a write up of that talk. The intended audience is folks coming from class-based languages with keywords like “class”, “implements”, “abstract”. I specifically programmed PHP prior to starting Go.

All inline code snippets will be italicized in this blog post.

0. Terminology

“Go has types and values rather than classes and objects.”

It is first important to understand that you can use object-oriented programming principles without having objects. The term “object” comes with a lot of implied meaning that is not possible with Go.

In Go, we have values, whereas traditional OOP languages have objects. Let’s differentiate the two.

Object vs. Value

An object is an instance of a class. The object is accessed through a named reference.

<?php

This PHP code illustrates that both $object and $reference point to the same instantiation of SomeObject.

Outside of being able to be accessed by various named references, objects also include key concepts like inheritance & subclasses that is not possible with Go. Thus, when learning Go, it is best to sever our ties from objects, and focus on using the correct terminology.

A value is exactly just that, a value. For example, a struct is a value. Each instance of a struct is copied when passed around.

package main
import ("fmt")

In the example above, you can see that when we assigned copy.Field, value.Field never changes its value. When we want to reference the same instance, similar to C, we have pointers to explicitly do so.

Types & Method Set

Now that we know why Go doesn’t have objects, let’s figure out how to operate on an instance of a type, specifically a struct type.

Types are associated with a method set. Each method in the method set can operate on the given receiver.

type SomeStruct struct{
Field string
}

Here, we see that method foo operates on the same instance of someStruct and changes its Field value.

Again. Go does not have objects, but we can see similarities between method receivers and class methods.

Now that we got down terminology, we can finally dive into applicable OOP patterns we can use to break up our Go code.

1. enCAPSulation

Encapsulation is the mechanism of hiding of data implementation by restricting access to public methods.

Traditionally, encapsulation in class-based OOP is achieved through private and public class variables / methods. In Go, encapsulation is achieved on a package level.

“Public” elements can be exported out of the package and indicated by capitalizing the first letter. Here, public is in quotes because the more accurate terminology is exported vs. unexported elements. Unexported elements are indicated with a lowercase first letter, and can only be accessed within its respective package.

Public/protected/private are keywords relative to classes, while exporting/importing are relative to packages.

package encapsulation

In the package encapsulation, Encapsulation (struct), Expose (method), and Unhide (method) are all exported and can be used from other packages.

package main

Here, we imported the package encapsulation as well as its exportable elements into the main package. Note how if we attempted to export the hide method, our compiler would produce an error.

2. Polymorphism

Polymorphism describes a pattern in object oriented programming in which classes have different functionality while sharing a common interface.

In Go, interfaces are implicitly satisfied. Interfaces are also types. These two sentences pack a lot of meaning, so lets break it down.

Interfaces are implicitly satisfied→ A type satisfies an interface when all the methods in the interface are included in the type’s method set. In Go, there is no implements keyword.

Interfaces are types → If a type implements an interface, that type will also satisfy anything type restricted by the interface it satisfies.

package polymorphism 

The main takeaway here is that the function SaySlogan can restrict its parameter to a SloganSayer type. Thus, any type that satisfies that interface will be accepted for that parameter.

package main 

We don’t need to worry about which presidential candidate is saying a slogan. As long as the type implements the SloganSayer interface, we can pass it into SaySlogan.

3. Composition

In Go, inheritance is not possible. Instead, we build our structs with composable and reusable elements through embedding.

Go allows us to embed types within interfaces or structs. Through embedding, we are able to forward the methods included from the inner type, to the outer type.

When we embed a type, the methods of that type become methods of the outer type, but when they are invoked the receiver of the method is the inner type, not the outer one.

package composition 

You’ll often hear the phrase “composition over inheritance”.

package main

4. Abstraction

Abstraction means working with something we know how to use without knowing how it works internally.

Similar to embedding structs within a struct, we can also embed interfaces within structs. Remember that any type that satisfied an interface also adopts that interface type.

package abstraction

The usefulness of interface embedding will be more evident when we see this being used.

package main

And that’s it! Now that you know how to borrow OOP principles you were originally familiar with, start writing some modular code. Below is a summary of how our OOP principles translate in Go.

If you found this helpful, please recommend / share! If you have questions / suggestions, feel free to reach out to my twitter @theamydance.

--

--

Youtuber & Distributed Systems-er

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store