Protocol oriented programming(POP) is a paradigm that has come into the limelight with the advent of Swift. Different languages over different periods have had some flavor of POP in them, but there are some traits unique in the POP central to Swift, which gives them a measurable advantage over OOP. That is not to say that OOP is flawed and or POP is the knight in the shining armor. POP simply extends OOP with a few new additions that help in writing better code ergo, better systems
a. Existing system example
b. Problem with Inheritance
c. Enter protocols
d. Protocol Extensions
e. Protocols & Value types
- Existing system example Let’s take a simple example to see how OOP and POP work on the same problem. Consider that we are contracted to build a vehicle that can be driven. The requirement is we will be asked to build many vehicles that will have different colors, number of wheels, different engine capacity, etc. Coming from the OOP world, the core of the solution would be something like this
Everything works fine. SmallCar, RaceCar both can be driven. They also can have their unique traits.(noOfSeats(), nitroBoosterCapacity() etc). No paradigm maps the real world as efficiently as OOP, and hence we can apply principles of real-world like inheritance in object modeling. That’s the beauty of it.
However, the real world is far from perfect, and that seeps into its derivative like OOP. Consider the same example above. The customer has now contracted you to build the ability to have wipers for the existing category of vehicles. So now Vehicle can be modified to have the ability to wipe
Courtesy, inheritance, all the categories of vehicles created hitherto can wipe its screen. RaceCar or SmallCar can simply call wipe() and startDriving() safely in the rains! The customer is now happy and since you are so awesome in building systems he now asks for introducing a new class of vehicle, A two-wheeler known as a motorcycle. The first reaction would be to subclass Motorcycle from Vehicle. In this case, Vehicle would not be useful. Why? Semantically a motorcycle is a vehicle so we should be able to use the Vehicle class. But the problem is if a Motorcycle inherits from Vehicle it would also inherit the ability to wipe(). Needless and absurd as well! So if we don’t extend Motorcycle from Vehicle, all the complex logic and business rules which are available through Vehicle namely in startDriving() will not be available to a Motorcycle. We could simply argue that ignore the ability to wipe() in a Motorcycle, but that’s knowingly introducing an association in the system which is not required. So what’s wrong with our design?
OOPs, OOP has done it again!
A subclass may not need all the features from its parent class. A child who has a rich, alcoholic father will happily choose to inherit his wealth but would keep his distance from his father's drinking vice(hopefully!). Similarly, a subclass may not require all the traits of its superclass. Since most of the languages do not support multiple inheritances we cannot break the superclass features into multiple smaller classes and have the subclasses inherit from the required ones.
OOP does not do a great job when it comes to this.
Problem with Inheritance
Inheritance is a great mechanism to reuse code and build software, but it is not particularly great when it comes to a selective association. The class that gets inherited is imbibed in the DNA of the subclass whether it likes it or not. It’s like a mobile data plan which comes with a host of great features that you are interested in but also with a few meh! features which you would have rather not wanted if sold individually. But since it’s part of the package you can’t escape it.
Protocols have been in existence since OOP itself and it offers another way of designing and modeling classes. Let’s consider the above example of vehicles and how we could design the same using protocols
As we can see, with protocols we have been able to break the dependency enforced by Inheritance on the class Motorcycle when it inherited Vehicle. Now any new category of Vehicle which does not require wipers won’t be burdened with the same. It simply does not conform to it. Don’t need it, don’t ask for it and so not burdened with it! For e.g. a scooter. The protocol used in Swift generally is implemented as an abstraction (virtual class, interface so on and so forth) in many other languages. Nothing really new there. Readers would have noticed here that the need to define the logic of driving is now on the concrete class which implements Vehicle i.e both RaceCar and SmallCar now has to define how to startDriving(). This can lead to the repetition of code and logic. Besides if protocols have been available in many other languages what’s so special about POP in Swift? The answer is Protocol extensions.
- Protocol Extensions The cornerstone for POP is protocol extension. Swift (v2.0 onwards) allows a protocol to have a generic behavior that can be overridden as well. This generic behavior allows every implementation to “inherit” this by default. If they don’t like the generic behavior, well, simple, change it i.e., override it. For e.g. the above example can now be written as
This is exactly like inheritance with the extra association of startWiper() now broken down to a need basis. Every implementation of Vehicle now has the complex startDriving() logic available by default i.e it has “inherited” the same from the protocol. Plus motorcycle does not have any association with Wipers. Thus protocol extensions help implementation to have the ability to “inherit” from a protocol and keep the dependency to an atomic level. i.e A protocol should contain only those contracts which an implementation or concrete object has to implement. Otherwise, it exhibits a fat interface problem. For e.g., Vehicle was a fat interface because it contained startWiper() which was not required for all subclasses.
- Protocols & Value types Swift advocates using value types over reference types wherever applicable. The use cases, benefits of both are well documented and won’t be covered here in this post. Value types like struct, enum can extend Protocols as well thus extending the benefits of designing using Protocols to even value types. This is one of the main reasons why Apple evangelizes using protocols over classes.
POP extends OOP to provide another level of abstraction which helps a developer to write better code and design reusable components. Everything has its place under the sun and OOP is certainly not to be totally replaced with POP. Only where required. The decision to use POP or OOP can be context-specific