Builder

Builder is a creational design pattern that lets you construct complex objects step by step. The pattern allows you to produce different types and representations of an object using the same construction code.

Example:

class Phone {
    var osVersion = 0
    var model = ""
    var cpuCodeName = ""
    var ramSize = 0
}

Builder

class PhoneBuilder {
    let phone = Phone()

    func getPhone() -> Phone {
        return self.phone
    }
}

Concrete Builder

class iPhoneXBuilder: PhoneBuilder {

    func setOSVersion() {
        self.phone.osVersion = 11
    }
    func setModel() {
        self.phone.model = "iPhone X"
    }
    func setCPUCodeName() {
        self.phone.cpuCodeName = "A11"
    }
    func setRamSize() {
        self.phone.ramSize = 3072
    }
}

class AndroidPhoneBuilder: PhoneBuilder{
    func setOSVersion() {
        self.phone.osVersion = 5
    }

    func setModel() {
        self.phone.model = "Android Phone"
    }
    func setCPUCodeName() {
        self.phone.cpuCodeName = "Mediatec"
    }
    func setRamSize() {
        self.phone.ramSize = 1024
    }
}

Director

class Director {
    var builder = PhoneBuilder()

    func setBuilder(builder: PhoneBuilder) {
        self.builder = builder
    }

    func getPhone() -> Phone {
        return self.builder.getPhone()
    }

    func constructPhone() {
        if let builder = builder as? iPhoneXBuilder {
            builder.setOSVersion()
            builder.setModel()
            builder.setCPUCodeName()
            builder.setRamSize()
        } else if let builder = builder as? AndroidPhoneBuilder {
            builder.setOSVersion()
            builder.setModel()
            builder.setCPUCodeName()
            builder.setRamSize()
        }
    }
}

Usage:

let iPhone = iPhoneXBuilder()
let androidPhone = AndroidPhoneBuilder()

let director = Director()

director.setBuilder(builder: iPhone)
director.constructPhone()
let phone1 = director.getPhone()
print("Model: \(phone1.model) ram: \(phone1.ramSize)")

director.setBuilder(builder: androidPhone)
director.constructPhone()
let phone2 = director.getPhone()
print("Model: \(phone2.model) ram: \(phone2.ramSize)")

Pros and Cons

  • ✅ You can construct objects step-by-step, defer construction steps or run steps recursively.
  • ✅ You can reuse the same construction code when building various representations of products.
  • ✅ Single Responsibility Principle. You can isolate complex construction code from the business logic of the product.
  • ❌ The overall complexity of the code increases since the pattern requires creating multiple new classes.