Forked from AliSoftware/struct_vs_inheritance.swift
Created
January 21, 2021 17:52
-
-
Save phucledien/9caa05d5e9382ddad52d58dff2ce0134 to your computer and use it in GitHub Desktop.
Swift, Struct & Inheritance: How to balance the will of using Struct & Value Types and the need for Inheritance?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// #!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 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment