Design Patterns - Creational



Builder

Builder is a creational mechanism that allows for construction of complex objects step by step. You can create different representations of an object via the same construction process.

Example

// Vehicle defines the fundamental attributes of a vehicle
protocol Vehicle : class {
    var name : String { get set }
    var color : Color { get set }
    var numberOfWheels : Int { get set }
    var numberOfDoors : Int { get set }
    var numberOfWings : Int { get set }
}

// VehicleBuilder allows you to build different kinds of vehicles 
protocol VehicleBuilder : Vehicle {
    func name(_ name: String) -> Self
    func color(_ color: Color) -> Self
    func numberOfWheels(_ numberOfWheels: Int) -> Self
    func numberOfDoors(_ numberOfDoors: Int) -> Self
    func numberOfWings(_ numberOfWinfs: Int) -> Self
}
extension VehicleBuilder {
    func name(_ name: String) -> Self {
        self.name = name
        return self
    }
    func color(_ color: Color) -> Self {
        self.color = color
        return self
    }
    func numberOfWheels(_ numberOfWheels: Int) -> Self {
        self.numberOfWheels = numberOfWheels
        return self
    }
    func numberOfDoors(_ numberOfDoors: Int) -> Self {
        self.numberOfDoors = numberOfDoors
        return self
    }
    func numberOfWings(_ numberOfWings: Int) -> Self {
        self.numberOfWings = numberOfWings
        return self
    }
}

// AnyVehicle is a concrete VehicleBuilder
class AnyVehicle : VehicleBuilder {
    var name : String = ""
    var color : Color = .clear
    var numberOfWheels : Int = 0
    var numberOfDoors : Int = 0
    var numberOfWings : Int = 0
}

// Build a green bicycle
let greenBicycle = AnyVehicle()
    .name("Bicycle")
    .color(.green)
    .numberOfWheels(2)
    .numberOfDoors(0)
    .numberOfWings(0)

// Build a red car
let redCar = AnyVehicle()
    .name("Car")
    .color(.red)
    .numberOfWheels(4)
    .numberOfDoors(4)
    .numberOfWings(0)

// Build a blue plane
let bluePlane = AnyVehicle()
    .name("Plane")
    .color(.blue)
    .numberOfWheels(20)
    .numberOfDoors(8)
    .numberOfWings(2)

Prototype

Prototype allows a copy of a complex object to be created in its existing state without introducing additional dependencies. The NSCopying protocol exists to serve this exact purpose.

Example

// Prototype extends the intention of NSCopying by casting the re-created copy
// from Any to a specific type
protocol Prototype : NSCopying {
    func clone() -> Self
}
extension Prototype {
    func clone() -> Self {
        copy() as! Self
    }
}

// TicTacToe simulates a 3x3 game of tic-tac-toe with 2 players
class TicTacToe {
    enum Move : Int, CaseIterable, CustomStringConvertible {
        case x, o
        var next : Self {
            Self(rawValue: ((rawValue+1)%Self.allCases.count))!
        }
        var description: String {
            switch self {
            case .x: return "x"
            case .o: return "o"
            }
        }
    }
    enum Value : CustomStringConvertible {
        case none, some(Move)
        var description: String {
            switch self {
            case .none: return "."
            case let .some(move): return move.description
            }
        }
    }
    var board : [[Value]] = Array(repeating: Array(repeating: .none, count: 3), count: 3)
    var currentMove : Move = .x
    func play(_ row: Int, _ col: Int) -> Bool {
        switch (row, col) {
        case (0..<board.count, 0..<board.first!.count):
            if case .none = board[row][col] {
                board[row][col] = .some(currentMove)
                currentMove = currentMove.next
                return true
            }
            fallthrough
        default:
            return false
        }
    }
}

// Have TicTacToe conform to Prototype & define a convenience
// initializer to replicate the current game
extension TicTacToe : Prototype {
    convenience init(_ board: [[Value]], _ currentMove: Move) {
        self.init()
        self.board = board
        self.currentMove = currentMove
    }
    func copy(with zone: NSZone? = nil) -> Any {
        return TicTacToe(board, currentMove)
    }
}


// Use Case:
// Create a game of tic-tac-toe
let game = TicTacToe()

// Make a few moves
_ = game.play(0, 2)
_ = game.play(1, 1)
_ = game.play(2, 2)
print("Original Game:\n\(game.board)")

// Clone the original game
let clonedGame = game.clone()

// And continue play...
clonedGame.play(0, 1)
print("Cloned Game:\n\(clonedGame.board)")

// Console Output:
Original Game:
[[., ., x], [., o, .], [., ., x]]
Cloned Game:
[[., o, x], [., o, .], [., ., x]]