Builder pattern is used to abstract the complex objects building process from the client and also this pattern is responsible for building objects step-by-step. Builder pattern also abstract complex object creation from the user.
NOTES
In this article, we discuss the implementation of the Builder Pattern in Go.
See the Builder in other languages in the “Other Code Implementations” section. Or, use the link below to check the details of the Builder Pattern-
Implementation
Here are the simple steps to implement Builder pattern in Go.
- Create the item structs and implement the getter methods for the properties.
- Create a builder interface.
- Create builder stucts and implement the builder interface.
- Create a producer to produce classes using the builders.
Check the examples below to see the full implementation.
Examples
Let’s look at a few examples of the builder pattern implementation.
Example #1: Vehicle Builder
Consider a builder that generates objects of vehicles, like, cars, planes, etc.
Car Struct
- Create file named “car.go”.
- Declare a struct named “Car”. Define properties for – wheel, engine, seat, door, and interior.
- Define a method named “NewCar”. Accept params for property values as param. Set the values in the method and then return the new car object.
- Define getter methods for the struct.
// car.go
package main
import "fmt"
type Car struct {
wheel int
engine int
seat int
door int
interior bool
}
func NewCar(noOfWheel int, noOfEngine int, noOfSeat int, noOfDoor int, interior bool) (car *Car) {
car = &Car{}
car.wheel = noOfWheel
car.engine = noOfEngine
car.seat = noOfSeat
car.door = noOfDoor
car.interior = interior
return
}
func (car *Car) GetDoor() int {
return car.door
}
func (car *Car) GetEngine() int {
return car.engine
}
func (car *Car) GetSeat() int {
return car.seat
}
func (car *Car) GetWheel() int {
return car.wheel
}
func (car *Car) IsInterior() bool {
return car.interior
}
func (car *Car) ToString() string {
return fmt.Sprintf("%v%v%v%v%v%v%v%v%v%v", "Car: Wheel -> ", car.wheel, " | Engine -> ", car.engine, " | Seat -> ", car.seat, " | Door -> ", car.door, " | Interior -> ", car.interior)
}
Plane Struct
- Create a file named “plane.go”.
- Declare struct named “Plane” and define properties.
- Define a method named “NewPlane”. Set the values in the method and then return the new car object.
- Define getter methods for the struct properties.
// plane.go
package main
import "fmt"
type Plane struct {
wheel int
engine int
seat int
door int
wing int
interior bool
}
func NewPlane(noOfWheel int, noOfEngine int, noOfSeat int, noOfDoor int, wing int, interior bool) (plane *Plane) {
plane = &Plane{}
plane.wheel = noOfWheel
plane.engine = noOfEngine
plane.seat = noOfSeat
plane.door = noOfDoor
plane.wing = wing
plane.interior = interior
return
}
func (plane *Plane) GetDoor() int {
return plane.door
}
func (plane *Plane) GetEngine() int {
return plane.engine
}
func (plane *Plane) GetSeat() int {
return plane.seat
}
func (plane *Plane) GetWheel() int {
return plane.wheel
}
func (plane *Plane) GetWing() int {
return plane.wing
}
func (plane *Plane) IsInterior() bool {
return plane.interior
}
func (plane *Plane) ToString() string {
return fmt.Sprintf("%v%v%v%v%v%v%v%v%v%v%v%v", "Plane: Wheel -> ", plane.wheel, " | Engine -> ", plane.engine, " | Seat -> ", plane.seat, " | Door -> ", plane.door, " | Wing: ", plane.wing, " | Interior -> ", plane.interior)
}
Vehicle Builder Interface
- Create a file named “vehicle_builder.go”.
- Define an interface “VehicleBuilder”. Define methods for setting/adding elements. This interface will be used the “CarBuilder” and “PlaneBuilder”.
// vehicle_builder.go
package main
type VehicleBuilder interface {
AddDoor(noOfDoor int)
AddEngine(noOfEngine int)
AddInterior()
AddSeat(noOfSeat int)
AddWheel(noOfWheel int)
AddWing(noOfWing int)
}
Car Builder
- Create a file named “car_builder.go” for the car builder.
- Define struct “CarBuilder” and define properties – wheel, engine, seat, etc.
- Define a method “NewCarBuilder” to generate a new car.
- Implement the “VehicleBuilder” interface by adding the add methods for “CarBuilder”.
// car_builder.go
package main
import "fmt"
type CarBuilder struct {
wheel int
engine int
seat int
interior bool
door int
}
func NewCarBuilder() (carBuilder *CarBuilder) {
carBuilder = &CarBuilder{}
return
}
func (carBuilder *CarBuilder) AddDoor(noOfDoor int) {
fmt.Printf("Add %d doorn", noOfDoor)
carBuilder.door += noOfDoor
}
func (carBuilder *CarBuilder) AddEngine(noOfEngine int) {
fmt.Printf("Add %d enginen", noOfEngine)
carBuilder.engine += noOfEngine
}
func (carBuilder *CarBuilder) AddInterior() {
fmt.Println("Add interior")
carBuilder.interior = true
}
func (carBuilder *CarBuilder) AddSeat(noOfSeat int) {
fmt.Printf("Add %d seatn", noOfSeat)
carBuilder.seat = noOfSeat
}
func (carBuilder *CarBuilder) AddWheel(noOfWheel int) {
fmt.Printf("Add %v wheeln", noOfWheel)
carBuilder.wheel += noOfWheel
}
func (carBuilder *CarBuilder) AddWing(noOfWings int) {
panic("Can not add wings")
}
func (carBuilder *CarBuilder) Build() *Car {
car := NewCar(carBuilder.wheel, carBuilder.engine, carBuilder.seat, carBuilder.door, carBuilder.interior)
return car
}
Plane Builder
- Create a file named “palne_builder.go” for the plane builder.
- Define struct “PlaneBuilder” and define properties – wheel, engine, seat, etc.
- Define a method “NewPlaneBuilder” to generate a new car.
- Implement the “VehicleBuilder” interface by adding the add methods for “PlaneBuilder”.
// plane_builder.go
package main
import "fmt"
type PlaneBuilder struct {
wheel int
engine int
seat int
interior bool
door int
wing int
}
func NewPlaneBuilder() (planeBuilder *PlaneBuilder) {
planeBuilder = &PlaneBuilder{}
return
}
func (planeBuilder *PlaneBuilder) AddDoor(noOfDoor int) {
fmt.Printf("Add %d doorn", noOfDoor)
planeBuilder.door += noOfDoor
}
func (planeBuilder *PlaneBuilder) AddEngine(noOfEngine int) {
fmt.Printf("Add %d enginen", noOfEngine)
planeBuilder.engine += noOfEngine
}
func (planeBuilder *PlaneBuilder) AddInterior() {
fmt.Println("Add interior")
planeBuilder.interior = true
}
func (planeBuilder *PlaneBuilder) AddSeat(noOfSeat int) {
fmt.Printf("Add %d Seatn", noOfSeat)
planeBuilder.seat = noOfSeat
}
func (planeBuilder *PlaneBuilder) AddWheel(noOfWheel int) {
fmt.Printf("Add %d wheelsn", noOfWheel)
planeBuilder.wheel += noOfWheel
}
func (planeBuilder *PlaneBuilder) AddWing(noOfWings int) {
fmt.Printf("Add %d wingn", noOfWings)
planeBuilder.wing += planeBuilder.wing
}
func (planeBuilder *PlaneBuilder) Build() *Plane {
plane := NewPlane(planeBuilder.wheel, planeBuilder.engine, planeBuilder.seat, planeBuilder.door, planeBuilder.wing, planeBuilder.interior)
return plane
}
Vehicle Producer
- Create a file named “vehicle_producer.go” for the producer.
- Define “BuildCar” method, which takes “CarBuilder” object as param, sets params, and then returns the object.
- Also, define “BuildPlane” for the “PlaneBuilder”.
// vehicle_producer.go
package main
type VehicleProducer struct {
}
func NewVehicleProducer() (vehicleProducer *VehicleProducer) {
vehicleProducer = &VehicleProducer{}
return
}
func (vehicleProducer *VehicleProducer) BuildCar(carBuilder *CarBuilder) *CarBuilder {
carBuilder.AddWheel(4)
carBuilder.AddEngine(1)
carBuilder.AddDoor(4)
carBuilder.AddSeat(4)
carBuilder.AddInterior()
return carBuilder
}
func (vehicleProducer *VehicleProducer) BuildPlane(planeBuilder *PlaneBuilder) *PlaneBuilder {
planeBuilder.AddWheel(3)
planeBuilder.AddEngine(2)
planeBuilder.AddDoor(4)
planeBuilder.AddSeat(120)
planeBuilder.AddInterior()
planeBuilder.AddWing(2)
return planeBuilder
}
Demo
To use the implementation follow the steps below-
- Create a file named “main.go” and define the “main” function.
- Create a new “VehicleProducer”.
- For the car, generate a “CarBuilder” object using “NewCarBuilder” method.
- Pass the “CarBuilder” object to “BuildCar” method of producer. That will set all the values to the “CarBuilder” object.
- Finally, call the “build()” method of the “CarBuilder” object.
- Follow the same for “Plane”.
// main.go
package main
import "fmt"
func main() {
vehicleProducer := VehicleProducer{}
fmt.Println("Building Car:")
carBuilder := NewCarBuilder()
vehicleProducer.BuildCar(carBuilder)
car := carBuilder.Build()
fmt.Printf("Final Result:n%+vn", *car)
fmt.Println("Building Plane:")
planeBuilder := NewPlaneBuilder()
vehicleProducer.BuildPlane(planeBuilder)
plane := planeBuilder.Build()
fmt.Printf("Final Result:n%+v", *plane)
}
Output
We will the following output from the code above:
Building Car:
Add 4 wheel
Add 1 engine
Add 4 door
Add 4 seat
Add interior
Final Result:
{
wheel:4
engine:1
seat:4
door:4
interior:true
}
Building Plane:
Add 3 wheels
Add 2 engine
Add 4 door
Add 120 Seat
Add interior
Add 2 wing
Final Result:
{
wheel:3
engine:2
seat:120
door:4
wing:0
interior:true
}
Example #2: Request Builder (Simple Builder)
Let’s consider implementing a request-sending process. With this implementation, we want to build the request in multiple steps and then send the request at the end.
Request Struct
- Create a file named “request.go”.
- Declare a “struct” named “Request” and add the required properties(url, requestType, header, body, etc.).
- Declare constant “ReqeustType” to use as the value for the “requestType” property.
- Add the “Builder” in “Request” as a composition.
- Add some utility methods as per requirement (like, send() method here).
// request.go
package main
import "fmt"
type RequestType int
const (
GET RequestType = iota
POST
PUT
PATCH
DELETE
)
type Request struct {
url string
requestType RequestType
header map[string]string
body map[string]string
Builder
}
func (rcvr *Request) Send() {
fmt.Println("Sending Request...")
fmt.Printf("nURL: %v", rcvr.url)
fmt.Printf("nRequest Type: %v", rcvr.requestType)
fmt.Printf("nHeaders: %v", rcvr.header)
fmt.Printf("nBody: %v", rcvr.body)
// Write code for actually sending the request here
}
Builder Struct
- Create a file named “builder.go”.
- Declare a “struct” named “Builder” and add the required properties.
- Declare methods(SetUrl, SetRequestType, etc.) to set all the properties. Return the builder reference from all the set methods, so that we can chain the method calls.
- Create a method named “build()”. This method will create a new request object, set all the properties from the values of “Builder” and then return the “Request” object.
// builder.go
package main
type Builder struct {
url string
requestType RequestType
header map[string]string
body map[string]string
}
func (builder *Builder) SetUrl(url string) *Builder {
builder.url = url
return builder
}
func (builder *Builder) SetRequestType(reqeustType RequestType) *Builder {
builder.requestType = reqeustType
return builder
}
func (builder *Builder) AddHeader(key, value string) *Builder {
if builder.header == nil {
builder.header = make(map[string]string)
}
builder.header[key] = value
return builder
}
func (builder *Builder) AddBody(key, value string) *Builder {
if builder.body == nil {
builder.body = make(map[string]string)
}
builder.body[key] = value
return builder
}
func (builder *Builder) Build() (request Request) {
request = Request{}
request.url = builder.url
request.requestType = builder.requestType
request.header = builder.header
request.body = builder.body
return
}
Demo
- Create a file named “main.go”.
- Use the “Builder” of the “Request” struct.
- Call the setting methods (like SetUrl, SetRequest, etc.) to set the values. Call the “Build” method to build the desired object finally. We can use call chaining of the methods because the set methods return “builder” at the end.
- Finally, call the “Send” method to send the request.
// main.go
package main
func main() {
builder := (&Request{}).Builder.
SetUrl("https://bigboxcode.com/request-test").
SetRequestType(POST).
AddHeader("X-AUTH-TOKEN", "someTokeHere").
AddHeader("X-SOME-HEADER", "someRandomHeaderValueHere").
AddBody("unit_id", "99").
AddBody("code", "8BI4BO6CO2").
Build()
builder.Send()
}
Output
We will get the following output when we run the above code.
Sending Request...
URL: https://bigboxcode.com/request-test
Request Type: 1
Headers: map[X-AUTH-TOKEN:someTokeHere X-SOME-HEADER:someRandomHeaderValueHere]
Body: map[code:8BI4BO6CO2 unit_id:99]
Source Code
Use the following link to get the source code:
Other Code Implementations
Use the following links to check Builder pattern implementation in other programming languages.