Builder Pattern
The Builder Pattern is a creational design pattern that provides an abstraction over object construction. Instead of directly constructing an object with a large number of constructor arguments, the pattern splits the construction process into multiple smaller steps. Each step is handled by a separate builder, which allows for greater flexibility and configurability.
The key components of the Builder Pattern are:
- Product: Represents the complex object being built.
- Builder: Provides an interface for constructing the parts of the product.
- Concrete Builder: Implements the builder interface and constructs the parts of the product.
- Director: Orchestrates the construction process by using the builder interface.
Implementing the Builder Pattern in Go
Let's now dive into a practical example of how to implement the Builder Pattern in Go. We'll create a CarBuilder that allows users to build customized cars with various options. The car can have attributes such as color, engine type, and optional accessories like sunroofs and navigation systems.
package main
// Car represents the complex object being built.
type Car struct {
color string
engineType string
hasSunroof bool
hasNavigation bool
}
// CarBuilder provides an interface for constructing the parts of the car.
type CarBuilder interface {
SetColor(color string) CarBuilder
SetEngineType(engineType string) CarBuilder
SetSunroof(hasSunroof bool) CarBuilder
SetNavigation(hasNavigation bool) CarBuilder
Build() *Car
}
// NewCarBuilder creates a new CarBuilder.
func NewCarBuilder() CarBuilder {
return &carBuilder{
car: &Car{}, // Initialize the car attribute
}
}
// carBuilder implements the CarBuilder interface.
type carBuilder struct {
car *Car
}
func (cb *carBuilder) SetColor(color string) CarBuilder {
cb.car.color = color
return cb
}
func (cb *carBuilder) SetEngineType(engineType string) CarBuilder {
cb.car.engineType = engineType
return cb
}
func (cb *carBuilder) SetSunroof(hasSunroof bool) CarBuilder {
cb.car.hasSunroof = hasSunroof
return cb
}
func (cb *carBuilder) SetNavigation(hasNavigation bool) CarBuilder {
cb.car.hasNavigation = hasNavigation
return cb
}
func (cb *carBuilder) Build() *Car {
return cb.car
}
// Director provides an interface to build cars.
type Director struct {
builder CarBuilder
}
func (d *Director) ConstructCar(color, engineType string, hasSunroof, hasNavigation bool) *Car {
d.builder.SetColor(color).
SetEngineType(engineType).
SetSunroof(hasSunroof).
SetNavigation(hasNavigation)
return d.builder.Build()
}
func main() {
// Create a new car builder.
builder := NewCarBuilder()
// Create a car with the director.
director := &Director{builder: builder}
myCar := director.ConstructCar("blue", "electric", true, true)
// Use the car object with the chosen configuration.
// ...
}
In this example, we define the Car
struct, which represents the complex object we want to build. The CarBuilder
interface provides methods to configure different parts of the car, and the carBuilder
type implements the interface.
The Director
struct orchestrates the construction process and delegates the building tasks to the concrete builder (carBuilder
). The client code can use the Director
to construct a car with various options without being concerned about the construction details.
Conclusion 🥂
The Builder Pattern is a powerful design pattern that simplifies the construction of complex objects with multiple configuration options. By splitting the construction process into smaller steps, the pattern promotes code reusability, maintainability, and flexibility.
Top comments (2)
Hi there,
The example is very good. There is only one issue. The car attribute in carBuilder is a pointer and it is not being initialize.
Best Regard,
A.
I have a q uery..why not assign all those values in one call
like cb.Car.X = sth,
cb.Car.X = sth else