As a fun project, I'm wanting to model an electronic circuit.
Components inherit from a superclass (ElectronicComponent). Each subclass (e.g. Resistor) has certain methods to return properties (e.g. resistance), but may vary by the number of outlets (leads) they have, and what they are named.
Each outlet connects to a Junction, as follows.
class Lead: Hashable /* Hashable implementation omitted */
{
let id = UUID()
unowned let component: ElectronicComponent
weak var connection: Junction?
init(connecting component: ElectronicComponent, to connection: Junction? = nil)
{
self.component = component
self.connection = connection
component.connections.insert(self)
connection?.connections.insert(self)
}
}
@dynamicMemberLookup
class ElectronicComponent
{
let id = UUID()
var connections: Set<Lead> = []
let label: String?
init(label: String)
{
self.label = label
}
subscript<T>(dynamicMember keyPath: KeyPath<ElectronicComponent, T>) -> T
{
self[keyPath: keyPath]
}
func connect(lead: KeyPath<ElectronicComponent, Lead>, to junction: Junction)
{
let lead = self[keyPath: lead]
lead.connection = junction
connections.insert(lead)
}
}
class Resistor: ElectronicComponent
{
var outlet1, outlet2: Lead!
let resistance: Measurement<UnitElectricResistance>
init(_ label: String, resistance: Measurement<UnitElectricResistance>)
{
self.resistance = resistance
super.init(label: label)
outlet1 = Lead(connecting: self)
outlet2 = Lead(connecting: self)
}
}
let resistorA = Resistor("R1", resistance: .init(value: 100, unit: .ohms))
let junctionA = Junction(name: "A")
When connecting up a circuit in code, my goal is to be able to use a line like this…
resistorA.connect(lead: \.outlet2, to: junctionA)
I could implement the @dynamicMemberLookup code in each subclass, but I was hoping having it in the superclass to reduce boilerplate. Problem is the compiler is not allowing me to do this as the superclass doesn't know about the subclass properties, and at the call site, the subclass isn't seen as ElectronicComponent.
I've been doing trial and error with protocol conformance and other things, but hitting walls each time.
One possibility is replacing the set of outlets with a dictionary, and using Strings instead of key paths, but would prefer not to, given the high probability of getting a property name wrong.
Another thing I haven't tried is creating and adopting a protocol with the method implemented in there. Another considered approach is using macros in the subclasses, but I'd like to see if there is a possibility of achieving the goal using my current approach, for learning as much as anything.