Protocol Oriented Programming
Object Oriented Programming has been around since the mid 80s and most of us use it every day. It allows us to model real world situations in our programs. Like you have a car management app for a big company. A car will be represented by a class ‚Car‘ and you create objects for every car in your car pool. And with abstraction you can easily model more complex things. Maybe beside typical cars you have trucks or motorcycles. All of them have motors but not all have doors or other traits.
As you can see this concept makes programming a lot easier and that’s the reason why the most of us use object oriented programming languages. Swift is object oriented too but there are some downsides.
The dark side of object oriented design 🌑 😈
Complexity
A Motorcycle is a TwoWheeled which is a MotorVehicle which is a Driveable which is Transportable. In large applications the inheritance tree can quickly grow and get very complex. New programmers in a project might need some time to get around. This is a big problem. We should always try to reduce complexity and make it easy to implement new features.
Concurrency
You might want to use threads to make your software more performant. But things will get difficult when it comes to handle your data. Classes have a „shared state“ among all threads: The data of your classes lies in the heap-memory. While every thread has its own stack, the heap is shared. When multiple threads try to manipulate data of an object, things get very difficult. You end up creating a mutex which is on a high cost of performance and error prone (deadlocks).
Implementation of Super-Classes
Imagine, you would like to implement an electro-scooter. An electro-scooter is a MotorVehicle. Now MotorVehicle forces you to implement the method „openDoors()“ and „closeAllDoors()“. The guy who implemented the MotorVehicle class didn’t make thoughts about the situation that someday someone would like to implement an electro scooter. I’ve seen this before many times especially in larger complex projects. You might end up implement the methods with an empty body and a silly comment.
No abstract methods in Swift
In Java I’m able to declare an abstract classes with abstract functions. You can’t create an object of an abstract class but every implementation inherit the non-abstract methods defined in the class. Swift doesn’t support this. That’s why I recently came across such things in production code:
Use protocols and value-types!
Understanding what’s going on: Value Types vs. Reference Types
When you create an object of a class, your program allocates memory. This job will be done by the operating system. And because you might want to read or modify your data later, the operating system tells you where your data in the memory got stored. This is the „address“ where your data lies in the memory. The address is a hexadecimal number such as „0xCAFEBABE“. Later, when you access your data, you access your data by a reference to the address in the memory. When you pass the variable of your object to another method, only the reference of your data will be passed and not the data itself. That means when you modify your data in the method, the data will be modified everywhere else.
Value types will be allocated on the stack instead of the heap. When you call a method with your value-type, a new strack frame for the method-call will be created and your value-type will be copied. When you manipulate the data of your value-type, the data is just manipulated on the current stack-frame because it’s copied. That means it’s just manipulated in the called method and not in the method you come from.
Start with a protocol
First think about the requirements. Then code. This can save you a massive amount of refactoring time later. Protocols force you to make thoughts about the features you want to provide and what data is required in order to do that. Everyone is an API designer.
Another point is that it’s much easier for other programmers to develop against your well defined protocol. I experienced huge classes with lots of helper functions and I had to figured out first what to call. Protocols hide complexity. Especially in larger projects with teams. It can be considered a kind of „self documenting code“.
Use „Traits“ instead of massive Superclasses
Traits are features which apply to all implementations. Think about a „Sailable“: All Sailboats ⛵️ are Sailable. In order to ride somewhere you have to „set the sail“. Assume the progress is the same for all „Sailable“s. You can provide a „set sail“ trait which applies for all sailboats.
When you create a new kind of Sailboat you just have to make it „Sailable“ and call the „setSail“ method.
Classes vs. Structs
Structs have some advantages over classes so I recommend to prefer them over classes for following reasons:
Problems in asynchronous environments
I already told you about the problems with classes when working with them in threads. Multiple threads might modify your data at the same time. You can prevent this by creating a mutex but this thread-locks are not very performant and really error prone (deadlocks).
Reference Counting
If you haven’t heard about reference counting before I recommend this article by Ray Wenderlich: https://www.raywenderlich.com/134411/arc-memory-management-swift
Swift handles memory management automatically with reference counting. When you create an object from a class, the count will be incremented to 1. Sometime later when there’s no reference anymore (when you’re done with your work) the count will be decremented to 0. If the count is 0, the object will get deleted.
As I told you above, value types will be allocated on the stack. Value types have no references and therefore you can’t count any references. The instance of a struct for example will remain on the stack until the method is over and the stack frame gets popped.
This situation is much more simple for the compiler. With reference counting you might get memory leaks sometimes. And you don’t have to worry about strong or weak relationships.
Performance
Structs are much for performant than classes. In this example structs are 50.000 times faster https://stackoverflow.com/a/24243626
The stack-access for value types is faster and you don’t have the reference counting overhead.
Structs are so amazing! I never use classes anymore
Even the amazing performant structs have limits. Think about data structures. A binary tree for example. You can’t use a struct in a struct again. In this case you have to use classes.
And it wouldn’t make sense for singletons to use structs because you never want to compare them or pass them around.
Conclusion
Structs have some advantages over classes. But maybe classes are the better approach for your feature you want to implement. And sometimes protocols don’t make sense: http://chris.eidhof.nl/post/protocol-oriented-programming/.
My number one rule is: Pick the right one for the job.