A computer is a complex machine made up of several hardware components. Then there is a software layer which efficiently makes use of these components to perform the desired task. These complex machines, many of the times, talk to each other to share the information or to delegate some tasks. All these tasks, at some point in time, deal with the data. The study of this whole ecosystem is widely known as Computer Science. A field of science where the entire focus is to automate a set of tasks. We keep on evolving and improving the existing ecosystem and expect it to do more for us. Every now and then there are commendable improvements in the hardware components. Subsequently, we evolve our software methodologies to fully utilize these hunky components. We invent Operating systems, Kernels, Compilers and gradually improve them to match up with the hardware. To build these powerful pieces of software, new programming languages evolve, with more features and more capabilities. Every programming language exists for the set of purposes and supports one or more programming paradigms.
One such paradigm is an Object-Oriented Programming (OOP), introduced in the early 1960s and has been widely used since then. This paradigm demanded us to think everything in terms of the objects. OOP has a tremendous capability of solving real-world problems through its core principles – Abstraction, Encapsulation, Inheritance & Polymorphism. Due to this, many programming languages have topped OOP as a prime paradigm. OOP languages like C++, JAVA, Objective-C etc. have supported the core principles through Classing, Subclassing, Method overloading, Abstracting (protocols) etc.
There are some caveats though –
- All of these features are solely available to the objects (reference types). Value types like structs and enums cannot utilize these features and hence they remain nothing much but just a data store.
- We lose a big advantage of value types being a thread-safe. Resultantly, we relentlessly focus on to tackle thread safety with objects(references).
- Also, most of the OOP languages do not support multiple-inheritance due to the complexity of its nature which causes irrelevant subclassing. For example: Suppose, a Singer and an Actor inherit from their parent, Artist, they both get the common functionality implemented by the Artist for free.
But if the requirement says that some singers could be an actor too then the inheritance tree would look something like –
Here, we are making every Singer an Actor too, which forces irrelevant functionality when a Singer is not an Actor.
There should be a way for these amazing OOP features to be available for value types too so that we do not have to worry about the cost of tackling the thread safety. Also, if we could bring the multiple-inheritance without much complexity for our types, then we could totally avoid the problem mentioned above.
Solution – A Protocol-Oriented approach
Time demands a different thinking approach. Thinking beyond OO way. A Protocol-Oriented approach. But first, What is Protocol? A protocol is a set of rules and requirements (methods and properties), which a type(value or reference) needs to fulfill. Consider protocol as an abstract blueprint of its requirements. A type can implement these requirements accordingly and said to conform to a protocol. Additionally, a type can conform to multiple protocols.
Let’s try to solve the above problem through a protocol-oriented approach –
We declared three protocols, Artist, Actor and Singer. Actor and Singer inherit from Artist protocol. Each protocol has declared their requirements. For example, a type conforming to Artist protocol needs to define name and age in its implementation. Similarly, a type conforming to Singer protocol needs to provide the implementation for name, age, songs and addSong().
Next, we defined a class named OnlySinger which conforms to Singer protocol. We defined another class SingerActor which inherits from OnlySinger such that it gets all the implemented functionality of Singer protocol and also made it conform to Actor protocol so that it can implement the required functionality of it.
This is how it looks in Swift –
This approach looks much cleaner as against to the subclassing approach. We are not leaking any extra functionality. This is a Protocol-Oriented approach where the major things are driven by the protocols. They set the rules and requirements and types need to implement those as per their need.
An advantage with protocols is that they can be extended to provide a default implementation of its requirements. For example, if some requirements of the protocol have common implementation across all of its conforming types, those requirements can be implemented by the protocol itself. All the conforming types get the default implementation for free and they do not need to implement it again.
A protocol is a language construct available in Swift and Objective-C programming language. Not all the features of it are available in Objective-C though. Though Swift supports OOP and Functional programming paradigms, it is a Protocol-Oriented language at its heart. Most of the value types in Swift like Array, Dictionary, String etc are based on protocols and built upon this new paradigm.
Swift surely gave birth to the new programming paradigm to overcome the caveats of OOP. Can POP replace OOP completely? I think POP is not here to replace OOP, it just overcomes some of OOP’s pitfalls. They together form a solid pair.
I hope you found this post helpful. Thanks for reading.