Swifty Waves

Design Patterns -> Builder

From Wikipedia: > The builder pattern is an object creation software design pattern. Unlike the abstract factory pattern and the factory method pattern whose intention is to enable polymorphism, the intention of the builder pattern is to find a solution to the telescoping constructor anti-pattern that occurs when the increase of object constructor parameter combination leads to an exponential list of constructors. Instead of using numerous constructors, the builder pattern uses another object, a builder, that receives each initialization parameter step by step and then returns the resulting constructed object at once.

Another definition is:

The builder pattern is used to create complex objects with constituent parts that must be created in the same order or using a specific algorithm. An external class controls the construction algorithm.

A swift example is:

For Swift, there are several examples with minor differences. Check [2] and [3] for two very similar examples. The example in [2] we have 2 diffent objects, the class and the build itself. In option [3], it's all done inside the class.

Option [2]

class DeathStarBuilder {
    var x: Double?
    var y: Double?
    var z: Double?

    typealias BuilderClosure = (DeathStarBuilder) -> ()

    init(buildClosure: BuilderClosure) {
        buildClosure(self)
    }
}

struct DeathStar : CustomStringConvertible {
    let x: Double
    let y: Double
    let z: Double

    init?(builder: DeathStarBuilder) {
        if let x = builder.x, let y = builder.y, let z = builder.z {
            self.x = x
            self.y = y
            self.z = z
        } else { return nil }
    }
}

//usage
let empire = DeathStarBuilder { builder in
    builder.x = 0.1
    builder.y = 0.2
    builder.z = 0.3
}

let deathStar = DeathStar(builder:empire)

Option [3]:

class Theme : ThemeProtocol {
    var backgroundColor:UIColor?
    var textColor:UIColor?
    
    typealias buildThemeClosure = (Theme) -> Void
    
    init(build:buildThemeClosure) {
        build(self)
    }
}

//usage
let darkTheme = Theme(build: {
    $0.backgroundColor = UIColor.blackColor()
    $0.textColor = UIColor.whiteColor()
})

The limitation of option 2 is that the properties need to be public. The properties can not be private.

The option [3] it's very similar with the current technique to initialise UI classes, as we can see here:

 let helloWorldLbl:UILabel = {  
    let lbl = UILabel()
    lbl.text = self.someFunctionToDetermineText()
    lbl.font = UIFont.preferredFontForTextStyle(UIFontTextStyleBody)
    lbl.textColor = self.myAppTheme.textColor()
    lbl.textAlignment = .Center
    return lbl
}()

In terms of create code snippets, we can have these:

class <#XPTO#>Builder {

    var x: String?
    var p: Int?
    var t: Bool = false

    typealias BuilderClosure = (<#XPTO#>Builder) -> ()

    init(buildClosure: BuilderClosure) {
        buildClosure(self)
    }
}

class <#XPTO#> {

    let x: String
    let p: Int
    let t: Bool

    init?(builder: <#XPTO#>Builder) {
        if let x = builder.x, let p = builder.p {
            self.x = x
            self.p = p
            self.t = t
        } else {
            return nil
        }
    }
}

Side note: We are now not using the Builder pattern very often. I can't remember exactly why but I think it's because Swift's default parameter capability. This makes that a class initialiser with multiple parameters will not be massive, ugly and difficult to use.

Resources

1 - https://en.wikipedia.org/wiki/Builder_pattern

2 - https://github.com/ochococo/Design-Patterns-In-Swift#creational

3 - https://medium.com/swift-programming/the-builder-pattern-in-swift-770d9cc1ac41

4 - https://github.com/kingreza/Swift-Builder

5 - https://www.raywenderlich.com/86053/intermediate-design-patterns-in-swift

Tagged with: