// #!Swift-1.1 import Foundation // MARK: - (1) classes // Solution 1: // - Use classes instead of struct // Issue: Violate the concept of moving model to the value layer // http://realm.io/news/andy-matuschak-controlling-complexity/ typealias JSONDict = [NSObject:AnyObject] class Vehicle1 { let model: String let color: String init(jsonDict: JSONDict) { model = jsonDict["model"] as String color = jsonDict["color"] as String } } class Car1 : Vehicle1 { let horsepower: Double let license_plate: String override init(jsonDict: JSONDict) { super.init(jsonDict: jsonDict) horsepower = jsonDict["horsepower"] as Double license_plate = jsonDict["license_plate"] as String } } class Bicycle1 : Vehicle1 { let chainrings: Int let sprockets: Int override init(jsonDict: JSONDict) { super.init(jsonDict: jsonDict) chainrings = jsonDict["chainrings"] as Int sprockets = jsonDict["sprockets"] as Int } } // MARK: - (2) struct + composition // Solution 2: // - keep value types // - use composition. // Issue: We violate the encapsulation principle, exposing the internal composition to the outside world struct Vehicle2 { let model: String let color: String init(jsonDict: JSONDict) { model = jsonDict["model"] as String color = jsonDict["color"] as String } } struct Car2 { let vehicle: Vehicle2 let horsepower: Double let license_plate: String init(jsonDict: JSONDict) { vehicle = Vehicle2(jsonDict: jsonDict) horsepower = jsonDict["horsepower"] as Double license_plate = jsonDict["license_plate"] as String } } struct Bicycle2 { let vehicle: Vehicle2 let chainrings: Int let sprockets: Int init(jsonDict: JSONDict) { vehicle = Vehicle2(jsonDict: jsonDict) chainrings = jsonDict["chainrings"] as Int sprockets = jsonDict["sprockets"] as Int } } // MARK: - (3) struct, protocol + composition for parsing // Solution 3: // - keep value types, use a protocol // - use intermediate struct only for parsing to keep encapsulation // Issue: None… except code verbosity protocol Vehicle3 { var model: String { get } var color: String { get } } private struct VehicleFields3 : Vehicle3 { let model: String let color: String init(jsonDict: JSONDict) { model = jsonDict["model"] as String color = jsonDict["color"] as String } } struct Car3 : Vehicle3 { let model: String let color: String let horsepower: Double let license_plate: String init(jsonDict: JSONDict) { let vehicle = VehicleFields3(jsonDict: jsonDict) model = vehicle.model color = vehicle.color horsepower = jsonDict["horsepower"] as Double license_plate = jsonDict["license_plate"] as String } } struct Bicycle3 : Vehicle3 { let model: String let color: String let chainrings: Int let sprockets: Int init(jsonDict: JSONDict) { let vehicle = VehicleFields3(jsonDict: jsonDict) model = vehicle.model color = vehicle.color chainrings = jsonDict["chainrings"] as Int sprockets = jsonDict["sprockets"] as Int } } // MARK: - (4) struct, protocols + global function for parsing // Solution 4: [Does not compile] // - keep value types, use a protocol // - use a global function to fill the objects's fields conforming to the protocol // Issue: does not work (it seems we can't pass 'self' as inout in the init() method) // exposes the setter in the protocol and the structs anyway (so bad access protection) protocol Vehicle4 { var model: String { get set } var color: String { get set } } private func parseVehicle4Fields(inout obj: Vehicle4, jsonDict: JSONDict) { obj.model = jsonDict["model"] as String obj.color = jsonDict["color"] as String } struct Car4 : Vehicle4 { var model: String var color: String let horsepower: Double let license_plate: String init(jsonDict: JSONDict) { parseVehicle4Fields(&self, jsonDict) // Error: Car4 is not identical to Vehicle4 horsepower = jsonDict["horsepower"] as Double license_plate = jsonDict["license_plate"] as String } } struct Bicycle4 : Vehicle4 { var model: String var color: String let chainrings: Int let sprockets: Int init(jsonDict: JSONDict) { parseVehicle4Fields(&self, jsonDict) // Error: Bicycle4 is not identical to Vehicle4 chainrings = jsonDict["chainrings"] as Int sprockets = jsonDict["sprockets"] as Int } }