Design patterns in Golang — The Builder

Surya Reddy
3 min readJan 11, 2021


I always wondered, how should we apply our very dear “Design Patterns” in Golang?

Object-Oriented Programming in Go is weird. If you are a Java or a C# developer, you would find a lot of similarities in applying SOLID principles in your code. But Go is special and special for a reason.

We would be taking a closer look at how we can implement some famous design patterns in Go.

Builder pattern

The motivation behind the Builder pattern is to create a complex object piece-by-piece. Our goal in this blog is to provide APIs to create complex objects step-by-step.


Some objects are easy to create with a simple constructor while others need a lot of ceremonies.

When do we need this?

When you don’t need your end-user to provide millions of details in parameters of a constructor and you want to make his/her life easy by providing them with a simple way to create a complex object.


  1. Builder facets
  2. Builder parameters
  3. Functional builders

Builder facets


They are more than one builder working in tandem to create an object.


Sometimes, our object is complex and the creational process requires more than one builder class. Segregating the builder into multiple builders also gives us control over the properties of a struct that a user can use.

Show me the code!


Suppose we want to create a fluent API that builds a person object like this:

We start with creating a Person struct and 3 builders PersonBuilder PersonAddressBuilder and PersonJobBuilder

Notice that thePersonAddressBuilder and PersonJobBuilder refer to PersonBuilder

Next, we would declare the constructors for these builders.

Notice how Lives() and Works() refer to PersonBuilder while initalization

Now, why this is important?

This would enable us to set properties of Person which is part of PersonBuilder

Ok, how would we set properties now?

Glad you asked!
We would create some behaviours/functions/methods for PersonAddressBuilder and PersonJobBuilder that sets the properties of thePerson

and finally, a “Build” method that returns a Person object.

So, putting it all together!


Once an object of PersonBuilder is created we switch to Lives() that returns a PersonAddressBuilder which have member functions like At() and WithPostalCode() , since we are returning pointers in all the function definitions we can chain our function calls together (line 91–97)

This way we can jump between different builders and use their corresponding member functions to create a complex object.

The result (trivial) looks like this:


Builder Parameters:

Functional Builders: