The Builder Pattern 🏠

Jeremi Kaczmarczyk
Swiftier, faster, better
3 min readAug 8, 2016

Constructional patterns πŸ—

Before we dive into details of the Builder pattern let me explain what in general constructional patterns are for. They are used to create instances of objects so that process of creation is separated from actual usage of the object. They are especially useful when mixing objects instead of using inheritance. As you can see even though the patterns in the Design Patterns book are labeled as object oriented it seems we can have a lot of fun with them and Swifty protocol oriented programming.

Basics πŸ“š

With the builder pattern you can split the creation process of complex object and it allows to have different representations throughout the application.

Authors of the Design Patterns suggest to use the Builder in following cases:

  • When object creation algorithm should be independent of its subparts and logic of their creation
  • When there is need to create different representations of generated object

Swift implementation πŸ”¨

There are some key actors in the builder pattern which could differentiate in Swift from the classic object oriented implementation.

Builder β€” abstract interface for creating Product. In our case it could be written that way:

protocol Builder {
func createPart()
}

The builder could work with generic Product but I don’t think it would be useful in the scope of this example.

ConcreteBuilder β€” implementation of the Builder protocol to encapsulate a Product creation process. This should be class as builder is very reference type pattern. Except conformation to the Builder protocol it also has method to retrieve the Product.

class ConcreteBuilder: Builder {    func createPart() {
…
}
func getProduct() -> Product {
…
}
}

Director β€” it creates objects with the Builder interface. Director should be initialized with object that conforms to Builder protocol so any ConcreteBuilder we have created.

class Director {    let builder: Builder    init(builder: Builder) {
self.builder = builder
}
func construct() {
builder.createPart()
}
}

Product β€” represents complex object which is created. It can also contain a little bit of logic to join the parts together for each specific Product.

The usage of the Builder is very simple. We use it from Client like:

let concreteBuilder = ConcreteBuilder()
let director = Director(builder: concreteBuilder)
director.construct()
let product = concreteBuilder.getProduct()

As I mentioned earlier in that raw form it is very reference dependent pattern so it should be used to create very complex objects.

Code sample πŸ› 

The most obvious use for the builder is code that is somehow similar but creates different representation of object and the client knows about that implementation. You can also imagine as the Builder builds your object from defined parts. The most characteristic thing about it is Product being not abstract but concrete object so the client knows how to interact with it.

Let’s say we have really complex Workout class for an Exercise application.

class Workout {    let locationService: LocationService?
let timer: TimerService?
// More properties
let displayableParameters: [Displayable]?
init() {
setupAllTheThings()
}
private func setupAllTheThings() {
// Heavy setup goes here
}
}

Even if we have it written nicely (not like in the example above) setup of such class could be quite complex. The Builder pattern would go nice in here. First we can define our abstract builder:

protocol WorkoutBuilder {
func addLocationService()
func addTimerService()
// All needed methods
func setupDisplayableParameters()
}

And then we can define our concrete builder:

class RunningWorkoutBuilder: WorkoutBuilder {    let workout = RunningWorkout()    func addLocationService() {
workout.locationService = LocationService()
}
// A lot of setup code func getWorkout() -> RunningWorkout {
return workout
}
}

Then we have a class which creates the workout, the Director class. For example FlowController needs to pass workout to its ViewController. The FlowController could be not only Director but also Client in that case:

let builder = RunningWorkoutBuilder()
builder.addLocationService()
// Moar setup, kind of constructing it from various parts
workoutViewController = WorkoutViewController(
workout: builder.getWorkout()
)

That way we have only high level functions exposed plus if we need similar object like TestWorkout it is trivial to set it up.

Conclusion 🍾

I hope after reading this article you have cleared idea (or idea at all) about the Builder pattern. I do, so I guess it is success. I hope you enjoyed my second post, if you did stay tuned for more.

Coming up next week: The Abstract Factory 🏭

--

--