The process of making a fragrance is quite complex, that’s why we have a fragrance builder who will help config the default ingredient.

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. –From

//: 1. Simple FragranceBuilder
class Fragrance: CustomStringConvertible {
    let name: String
    let ingFlower: String
    let ingFruit: String
    let ingWood: String
    let ingSpecial: String
    let powerLevel: Int
    var description: String {
        return "Fragrance:\(name)\n" +
        "Flower:\(ingFlower)\n" +
        "Fruit: \(ingFruit)\n" +
        "Wood: \(ingWood)\n" +
        "Special Ingredience: \(ingSpecial)\n" +
        "Power Level: \(powerLevel)\n"
    }
    
    init(name:String, ingFlower: String, ingFruit: String, ingWood: String, ingSpecial:String, powerLevel: Int) {
        self.name = name
        self.ingFlower = ingFlower
        self.ingFruit = ingFruit
        self.ingWood = ingWood
        self.ingSpecial = ingSpecial
        self.powerLevel = powerLevel
    }
}

class FragranceBuilder {
    private var ingFlower = "Jasmine"
    private var ingFruit = "Strawberry"
    private var ingWood = "Sandalwood"
    private var ingSpecial = "Fish's tear"
    private var powerLevel = 0
    func setIngFlower(ingFlower: String){
        self.ingFlower = ingFlower
    }
    func setIngFruit(ingFruit: String){
        self.ingFruit = ingFruit
    }
    func setIngWood(ingWood: String){
        self.ingWood = ingWood
    }
    func setIngSpecial(ingSpecial: String){
        self.ingSpecial = ingSpecial
    }
    func setPowerLevel(powerLevel: Int){
        self.powerLevel = powerLevel
    }
    
    func buildFragrance(name: String) -> Fragrance {
        return Fragrance(name: name, ingFlower: ingFlower, ingFruit: ingFruit, ingWood: ingWood, ingSpecial: ingSpecial, powerLevel: powerLevel)
    }
}

let builder = FragranceBuilder()
builder.setIngFlower("Rose")
builder.setIngSpecial("First Raindrop in Spring")
let roseFragrance = builder.buildFragrance("Rose in Spring")
print(roseFragrance.description)


//: 2. Use specialized FragranceBuilderFactory to build different fragrance
enum FragranceType {
    case Floral
    case Fruity
    case Oceanic
}

class FragranceBuilderFactory {
    private var ingFlower = "Jasmine"
    private var ingFruit = "Strawberry"
    private var ingWood = "Sandalwood"
    private var ingSpecial = "Fish's tear"
    private var powerLevel = 0
    private init(){
        // do nothing
    }
    func setIngFlower(ingFlower: String){
        self.ingFlower = ingFlower
    }
    func setIngFruit(ingFruit: String){
        self.ingFruit = ingFruit
    }
    func setIngWood(ingWood: String){
        self.ingWood = ingWood
    }
    func setIngSpecial(ingSpecial: String){
        self.ingSpecial = ingSpecial
    }
    func setPowerLevel(powerLevel: Int){
        self.powerLevel = powerLevel
    }
    
    func buildFragrance(name: String) -> Fragrance {
        return Fragrance(name: name, ingFlower: ingFlower, ingFruit: ingFruit, ingWood: ingWood, ingSpecial: ingSpecial, powerLevel: powerLevel)
    }
    
    class func getFragranceBuilder(fragranceType: FragranceType) -> FragranceBuilderFactory {
        switch fragranceType {
        case .Floral: return FloralFrangranceBuilder()
        case .Fruity: return FruityFrangranceBuilder()
        case .Oceanic: return OceanicFrangranceBuilder()
        }
    }
}

class FloralFrangranceBuilder: FragranceBuilderFactory {
    override init(){
        super.init()
        self.ingFlower = "Rose"
        self.powerLevel = 99
    }
    
}
class FruityFrangranceBuilder: FragranceBuilderFactory {
    override init(){
        super.init()
        self.ingFruit = "Apple"
        self.powerLevel = 99
    }

    
}
class OceanicFrangranceBuilder: FragranceBuilderFactory {
    override init(){
        super.init()
        self.ingWood = "Pine"
        self.powerLevel = 80
    }
}

let builder1 = FragranceBuilderFactory.getFragranceBuilder(FragranceType.Oceanic)
builder.setIngSpecial("Mermaid Scale")
let fragrance1 = builder.buildFragrance("Ocean Song")
print(fragrance1.description)


//: 3. Use closure to config the FragranceBuilder
struct TearInHeavenFragrance {
    let name: String
    let ingFlower: String
    let ingFruit: String
    let ingWood: String
    let ingSpecial: String
    let powerLevel: Int
    var description: String {
        return "Fragrance:\(name)\n" +
            "Flower:\(ingFlower)\n" +
            "Fruit: \(ingFruit)\n" +
            "Wood: \(ingWood)\n" +
            "Special Ingredience: \(ingSpecial)\n" +
        "Power Level: \(powerLevel)\n"
    }
    
    init(name: String, builder: TearInHeavenFragranceBuilder) {
        self.name = name
        self.ingFlower = builder.ingFlower
        self.ingFruit = builder.ingFruit
        self.ingWood = builder.ingWood
        self.ingSpecial = builder.ingSpecial
        self.powerLevel = builder.powerLevel
    }
}

class TearInHeavenFragranceBuilder {
    var ingFlower = "Jasmine"
    var ingFruit = "Strawberry"
    var ingWood = "Sandalwood"
    var ingSpecial = "Fish's tear"
    var powerLevel = 0
    init(builderHandler: (TearInHeavenFragranceBuilder) -> Void) {
        builderHandler(self)
    }
}
let builder2 = TearInHeavenFragranceBuilder { (builder) -> Void in
    builder.ingFlower = "White Lily"
    builder.powerLevel = 78
}

let fragrance2 = TearInHeavenFragrance(name: "Tear in Heaven", builder: builder2)
print(fragrance2.description)

builder