How to Use Protocols for Stucts to Emulate Classes Inheritance

Alternate approach to inheritance for Swift structs?

In Swift with struct you can create protocol for common task and also implement default implementation using protocol extension.

protocol Vehicle {
var model: String { get set }
var color: String { get set }
}

//Common Implementation using protocol extension
extension Vehicle {

static func parseVehicleFields(jsonDict: [String:Any]) -> (String, String) {
let model = jsonDict["model"] as! String
let color = jsonDict["color"] as! String
return (model, color)
}
}

struct Car : Vehicle {
var model:String
var color:String
let horsepower: Double
let license_plate: String
init(jsonDict: [String:Any]) {
(model, color) = Car.parseVehicleFields(jsonDict: jsonDict)
horsepower = jsonDict["horsepower"] as! Double
license_plate = jsonDict["license_plate"] as! String
}
}

struct Bicycle : Vehicle {
var model:String
var color:String
let chainrings: Int
let sprockets: Int
init(jsonDict: [String:Any]) {
(model, color) = Bicycle.parseVehicleFields(jsonDict: jsonDict)
chainrings = jsonDict["chainrings"] as! Int
sprockets = jsonDict["sprockets"] as! Int
}
}

Swift Struct idiom to replace OOP Type Inheritance Pattern?

You should be able to accomplish this with protocols. You would move the common logic into a Protocol and then create two classes that would conform to this protocol with different initializers. Then where you would refer to a particular object type, you could refer to the protocol instead.

protocol RectangleProtocol {
var left:Int {get}
var right:Int {get}
var top:Int {get}
var bottom:Int {get}
}

struct Rectangle: RectangleProtocol {
let left: Int
let right: Int
let top: Int
let bottom: Int

init(leftValue:Int, rightValue:Int, topValue:Int, bottomValue:Int) {
self.left = leftValue
self.right = rightValue
self.top = topValue
self.bottom = bottomValue
}
}

struct RationalRectangle: RectangleProtocol {
let left: Int
let right: Int
let top: Int
let bottom: Int

init(leftValue:Int, rightValue:Int, topValue:Int, bottomValue:Int) {
self.left = min(leftValue, rightValue)
self.right = max(leftValue, rightValue)
self.top = min(topValue, bottomValue)
self.bottom = max(topValue, bottomValue)
}
}

let rectangle: RectangleProtocol = Rectangle(leftValue: 4, rightValue 4, topValue: 8, bottomValue: 8)
let rationalRectangle: RectangleProtocol = RationalRectangle(leftValue: 4, rightValue:8, topValue: 7, bottomValue: 4)

// Now both of these represent a struct that conforms to the RectangleProtocol.

How to implement a Swift protocol across structs with conflicting property names

The desired feature from explicit interface implementations is that they are statically dispatched, right? If you use Description on a BusStop, it will be an optional string, but if you use Description on a Stop, it will be a non-optional string.

In Swift, extension members are statically dispatched, so you can make use of this to achieve something similar:

extension Stop where Self == BusStop {
// Since the type of "self" here is BusStop, "Description" refers to the one declared in BusStop
// not this one here, so this won't cause infinite recursion
var Description : String { return self.Description ?? "" }
}

extension Stop where Self == TrainStop {
var Latitude: Double { return self.Latitude ?? 0 }
var Longitude: Double { return self.Longitude ?? 0 }
}

This code shows that this works:

let busStop = BusStop(Code: "ABC", Description: "My Bus Stop", Latitude: 0, Longitude: 0)
print(type(of: busStop.Description)) // Optional<String>
let stop: Stop = busStop
print(type(of: stop.Description)) // String

However, I still don't think this is good Swift code. It is often bad to just directly translate an API from one language to another. If I were you, I would make Longitude, Latitude and Description in Stop to be all optionals.

Object oriented programming in C

You can implement polymorphism with regular functions and virtual tables (vtables). Here's a pretty neat system that I invented (based on C++) for a programming exercise: alt text

The constructors allocate memory and then call the class's init function where the memory is initialized. Each init function should also contain a static vtable struct that contains the virtual function pointers (NULL for pure virtual). Derived class init functions call the superclass init function before doing anything else.

A very nice API can be created by implementing the virtual function wrappers (not to be confused with the functions pointed to by the vtables) as follows (add static inline in front of it, if you do this in the header):

int playerGuess(Player* this) { return this->vtable->guess(this); }

Single inheritance can be done by abusing the binary layout of a struct: alt text

Notice that multiple inheritance is messier as then you often need to adjust the pointer value when casting between types of the hierarchy.

Other type-specific data can be added to the virtual tables as well. Examples include runtime type info (e.g. type name as a string), linking to superclass vtable and the destructor chain. You probably want virtual destructors where derived class destructor demotes the object to its super class and then recursively calls the destructor of that and so on, until the base class destructor is reached and that finally frees the struct.

Encapsulation was done by defining the structs in player_protected.h and implementing the functions (pointed to by the vtable) in player_protected.c, and similarly for derived classes, but this is quite clumsy and it degrades performance (as virtual wrappers cannot be put to headers), so I would recommend against it.

How do you implement protocol methods that return covariant Selfs?

As it says, you can't do this, and for good reason. You can't prove you'll keep your promise. Consider this:

class AnotherSubclass: Class {}
let x = AnotherSubclass().instance

So x should be AnotherSubclass according to your protocol (that's Self). But it'll actually be Subclass, which is a completely different type. You can't resolve this paradox unless the class is final. This isn't a Swift limitation. This limitation would exist in any correct type system because it allows an type contradiction.

On the other hand, something you can do is promise that instance returns some consistent type across all subclasses (i.e. the superclass). You do that with an associated type:

protocol Protocol {
typealias InstanceType
var instance: InstanceType {get}
}

class Class: Protocol {
var instance: Class {return Subclass()}
}

class Subclass: Class {}
class AnotherSubclass: Class {}
let x = AnotherSubclass().instance

Now x is unambiguously of type Class. (It also happens to be random other subclass, which is kind of weird, but that's what the code says.)

BTW, all of this usually suggests that you're using subclassing when you really shouldn't be. Composition and protocols would probably solve this problem better in Swift. Ask yourself if there's any reason that Subclass needs to actually be a subclass of Class. Could it be an independent type that conforms to the same protocol? All kinds of problems go away when you get rid of subclasses and focus on protocols.


I've been thinking about this more, and there may be a way to get what you're looking for. Rather than saying that all subclasses implement instance, attach instance as an extension. You can still override that if you want to return something else.

protocol Protocol {
init()
}

class Class: Protocol {
required init() {}
var instance: Class { return Subclass() }
}

extension Protocol {
var instance: Self { return self.dynamicType.init() }
}

class Subclass: Class {}

This dodges the inheritance problem (you can't create the same "AnotherClass returning the wrong type" this way).

Abstract classes in Swift Language

There are no abstract classes in Swift (just like Objective-C). Your best bet is going to be to use a Protocol, which is like a Java Interface.

With Swift 2.0, you can then add method implementations and calculated property implementations using protocol extensions. Your only restrictions are that you can't provide member variables or constants and there is no dynamic dispatch.

An example of this technique would be:

protocol Employee {
var annualSalary: Int {get}
}

extension Employee {
var biweeklySalary: Int {
return self.annualSalary / 26
}

func logSalary() {
print("$\(self.annualSalary) per year or $\(self.biweeklySalary) biweekly")
}
}

struct SoftwareEngineer: Employee {
var annualSalary: Int

func logSalary() {
print("overridden")
}
}

let sarah = SoftwareEngineer(annualSalary: 100000)
sarah.logSalary() // prints: overridden
(sarah as Employee).logSalary() // prints: $100000 per year or $3846 biweekly

Notice that this is providing "abstract class" like features even for structs, but classes can also implement the same protocol.

Also notice that every class or struct that implements the Employee protocol will have to declare the annualSalary property again.

Most importantly, notice that there is no dynamic dispatch. When logSalary is called on the instance that is stored as a SoftwareEngineer it calls the overridden version of the method. When logSalary is called on the instance after it has been cast to an Employee, it calls the original implementation (it doesn't not dynamically dispatch to the overridden version even though the instance is actually a Software Engineer.

For more information, check great WWDC video about that feature: Building Better Apps with Value Types in Swift



Related Topics



Leave a reply



Submit