Last active
July 30, 2020 20:59
-
-
Save iComputerfreak/6469385c6784667bc96d79c7eec454a3 to your computer and use it in GitHub Desktop.
Conforming to the Equatable protocol in a class hierarchy
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
import Foundation | |
// MARK: - Vehicle.swift | |
class Vehicle { | |
let name: String | |
init(name: String) { | |
self.name = name | |
} | |
} | |
// MARK: - Car.swift | |
class Car: Vehicle { | |
let seats: Int | |
init(name: String, seats: Int) { | |
self.seats = seats | |
super.init(name: name) | |
} | |
} | |
// MARK: - Bicycle.swift | |
class Bicycle: Vehicle { | |
let color: String | |
init(name: String, color: String) { | |
self.color = color | |
super.init(name: name) | |
} | |
} | |
// MARK: - Vehicle+Equatable.swift | |
extension Vehicle: Equatable { | |
static func == (lhs: Vehicle, rhs: Vehicle) -> Bool { | |
return type(of: lhs) == type(of: rhs) && lhs.isEqual(to: rhs) | |
} | |
@objc fileprivate func isEqual(to other: Any) -> Bool { | |
// other does not need to have the type Vehicle, just some subclass of it. Since it is fileprivate, that is always true. | |
let other = other as! Vehicle | |
return self.name == other.name | |
} | |
} | |
extension Car { | |
// This function will only be called, after checking, that self and other have the same type, so we can assume, that other is of type Car too. | |
override fileprivate func isEqual(to other: Any) -> Bool { | |
let otherCar = other as! Car | |
return super.isEqual(to: other) && self.seats == otherCar.seats | |
} | |
} | |
extension Bicycle { | |
// This function will only be called, after checking, that self and other have the same type, so we can assume, that other is of type Car too. | |
override fileprivate func isEqual(to other: Any) -> Bool { | |
let otherBicycle = other as! Bicycle | |
return super.isEqual(to: other) && self.color == otherBicycle.color | |
} | |
} | |
// MARK: - Tests | |
// MARK: Test Setup | |
let vehicle1 = Vehicle(name: "Vehicle 1") | |
let vehicle2 = Vehicle(name: "Vehicle") | |
let vehicle3 = Vehicle(name: "Vehicle") | |
let car1 = Car(name: "Car", seats: 5) | |
let car2 = Car(name: "Vehicle", seats: 2) | |
let car3 = Car(name: "Vehicle", seats: 5) | |
let bicycle1 = Bicycle(name: "Car", color: "black") | |
let bicycle2 = Bicycle(name: "Vehicle", color: "blue") | |
let bicycle3 = Bicycle(name: "Vehicle", color: "blue") | |
let allVehicles = [vehicle1, vehicle2, vehicle3] | |
// Contains all Vehicle x Vehicle pairs | |
let vehiclePairs = allVehicles.flatMap({ (vehicle: Vehicle) -> [(Vehicle, Vehicle)] in | |
var returnArray: [(Vehicle, Vehicle)] = [] | |
for v in allVehicles { | |
returnArray.append((vehicle, v)) | |
} | |
return returnArray | |
}) | |
// Contains all Vehicle x Vehicle pairs | |
let vehicleTriples = vehiclePairs.flatMap({ (pair: (Vehicle, Vehicle)) -> [(Vehicle, Vehicle, Vehicle)] in | |
var returnArray: [(Vehicle, Vehicle, Vehicle)] = [] | |
for v in allVehicles { | |
returnArray.append((pair.0, pair.1, v)) | |
} | |
return returnArray | |
}) | |
// MARK: Reflexivity | |
for vehicle in allVehicles { | |
assert(vehicle == vehicle) | |
} | |
// MARK: Symmetry | |
for pair in vehiclePairs { | |
if pair.0 == pair.1 { | |
assert(pair.1 == pair.0) | |
} else { | |
assert(pair.1 != pair.0) | |
} | |
} | |
// MARK: Transitivity | |
for triple in vehicleTriples { | |
if triple.0 == triple.1 && triple.1 == triple.2 { | |
assert(triple.0 == triple.2) | |
} | |
} | |
// MARK: Arbitrary Tests | |
// We didn't implement != ourselves, but Swift automatically generated it, as "!(==)", so we have to check it for correctness too. | |
assert(vehicle1 != vehicle2) | |
assert(vehicle1 != vehicle3) | |
assert(vehicle2 == vehicle3) | |
assert(car1 != car2) | |
assert(car1 != car3) | |
assert(car2 != car3) | |
assert(bicycle1 != bicycle2) | |
assert(bicycle1 != bicycle3) | |
assert(bicycle2 == bicycle3) | |
assert(vehicle1 != car1) | |
assert(vehicle1 != car2) | |
assert(vehicle1 != car3) | |
assert(vehicle1 != bicycle1) | |
assert(vehicle1 != bicycle2) | |
assert(vehicle1 != bicycle3) | |
assert(vehicle2 != car1) | |
assert(vehicle2 != car2) | |
assert(vehicle2 != car3) | |
assert(vehicle2 != bicycle1) | |
assert(vehicle2 != bicycle2) | |
assert(vehicle2 != bicycle3) | |
assert(vehicle3 != car1) | |
assert(vehicle3 != car2) | |
assert(vehicle3 != car3) | |
assert(vehicle3 != bicycle1) | |
assert(vehicle3 != bicycle2) | |
assert(vehicle3 != bicycle3) | |
assert(car1 != vehicle1) | |
assert(car1 != vehicle2) | |
assert(car1 != vehicle3) | |
assert(car1 != bicycle1) | |
assert(car1 != bicycle2) | |
assert(car1 != bicycle3) | |
assert(car2 != vehicle1) | |
assert(car2 != vehicle2) | |
assert(car2 != vehicle3) | |
assert(car2 != bicycle1) | |
assert(car2 != bicycle2) | |
assert(car2 != bicycle3) | |
assert(car3 != vehicle1) | |
assert(car3 != vehicle2) | |
assert(car3 != vehicle3) | |
assert(car3 != bicycle1) | |
assert(car3 != bicycle2) | |
assert(car3 != bicycle3) | |
assert(bicycle1 != vehicle1) | |
assert(bicycle1 != vehicle2) | |
assert(bicycle1 != vehicle3) | |
assert(bicycle1 != car1) | |
assert(bicycle1 != car2) | |
assert(bicycle1 != car3) | |
assert(bicycle2 != vehicle1) | |
assert(bicycle2 != vehicle2) | |
assert(bicycle2 != vehicle3) | |
assert(bicycle2 != car1) | |
assert(bicycle2 != car2) | |
assert(bicycle2 != car3) | |
assert(bicycle3 != vehicle1) | |
assert(bicycle3 != vehicle2) | |
assert(bicycle3 != vehicle3) | |
assert(bicycle3 != car1) | |
assert(bicycle3 != car2) | |
assert(bicycle3 != car3) | |
let hiddenBicycle = Bicycle(name: "Vehicle", color: "black") | |
assert(type(of: (hiddenBicycle as Vehicle)) == Bicycle.self) | |
assert((hiddenBicycle as Vehicle) != vehicle2) | |
print("Test finished.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment