Protocol Oriented Programming

Using Swift’s protocols to overcome multiple inheritance problems and improves modeling flexibility

Mushtaque Ahmed
6 min readDec 6, 2019
Thanks to Lukas for the illustration

Introduction

Protocol Oriented Programming or POP is a special feature in Swift as an answer to legacy inheritance and subclassing in other programming languages.

POP helps to add features in various classes of different types using protocols. It also strengthens a better architecture process by promoting solid principles of dependency inversion (check here).

As we know Swift doesn’t support multiple inheritances, but there are times when we need a class to inherit the behavior of different interfaces/classes in one so POP helps to achieve that using protocols.

We will see two types of protocol features:

  • Protocols with extension
  • Protocols with associated types

Here is a list of advantages of Protocols and how it differs with Inheritance:

  1. Interface reuse
  2. Customization
  3. Implementation reuse
  4. Usable with value types
  5. Modeling flexibility

1. Interface reuse

Interface reuse enables modularity. Both Inheritance and protocols provide these features. Subclasses inherit from their superclass and Protocol conforming classes from the protocol it's conforming to.

// Simple Intheritence Implementation.class Animal {
func waksOnNumberOfLegs() -> Int {
return 4
}
func hastail() -> Bool {
return true
}
func speaks {
print("")
}
}
class Dog : Animal {
overide func speaks {
print("Bow Bow")
}
}
class Cat : Animal {
overide func speaks {
print("Meow Meow")
}
}
let dog = Dog()
dog.waksOnNumberOfLegs() // 4
truedog.hastail() //tru
edog.speaks() // Bow Bow
let cat = Cat()
cat.waksOnNumberOfLegs() // 4
cat.hastail() // true

2. Customization

Subclasses can customize common methods and properties by overriding them (Look above code) . Protocol conforming classes from the protocol extensions it's conforming to. (Look code in Protocol extension section)

3. Implementation

Inheritance helps in implementation reuse. All subclasses can inherit common properties and methods. (As shown below). Subclasses inherit from their superclass and Protocol conforming classes from the protocol extension it's conforming to (Look code in Protocol extension section).

4. Usable with value types

Inheritance can be used only with class types whereas protocols can be used with value types.

5. Modeling flexibility

In Inheritance, we have to define the superclass of a class upfront and any change later could result in the change in whole subclass implementation. Whereas in protocol-oriented programming you can follow dynamic retroactive modeling of classes or structs. For more info on retroactive modeling check this video.

Protocol with extension

//Simple Protocols Implementationprotocol Animal {
func waksOnNumberOfLegs() -> Int
func hastail() -> Bool
func speaks
}
------------------------------------------------------
class Dog : Animal { func waksOnNumberOfLegs() -> Int {
return 4
}
func hastail () -> Bool{
return true
}
func speaks {
print("Bow Bow")
}
}
class Cat: Animal { func waksOnNumberOfLegs() -> Int {
return 4
}
func hastail() -> Bool {
return true
}
func speaks {
print("Meo Mew")
}
}

Now as you saw above this is a simple protocol implementation in which we can reuse Animal interface in both Cat and Dog Classes and customize it as we like it (Advantages 1 and 2).

Whereas we have to implement each methods separately in all conforming class as we cant implement default methods in the protocol body.

Now with the help of protocol extensions we can implement default methods in protocol bodies that enable Implementation reuse. See Below code (Advantage 3):

protocol Animal {
func waksOnNumberOfLegs() -> Int
func hastail() -> Bool
func speaks
}
------------------------------------------------------
extension Animal {
func waksOnNumberOfLegs() -> Int {
return 4
}
func hastail() -> Bool {
return true
}
}
------------------------------------------------------
class Dog : Animal {
func speaks {
print("Bow Bow")
}
}
struct Cat: Animal {
func speaks {
print("Meow Meow")
}
}
---------------------------------------------------
let dog = Dog()
dog.waksOnNumberOfLegs() // 4
dog.hastail() //true
dog.speaks() // Bow Bow
let cat = Cat()
cat.waksOnNumberOfLegs() // 4
cat.hastail() // true
cat.speaks() // Meow Meow

As mentioned above (Advantage 4) we can use value types like structs or enum to conform to protocol extensions. See how we have declared Cat above as struct rather than class.

POP helps us to achieve similar behavior of multiple inheritances. A class or struct can conform to one or more protocols and implement there methods and properties.

protocol Mammal {
func walksOnLand() -> Bool
func hasWarmBlooded() -> Bool
}
---------------------------------------------------
extension Mammal {
func walksOnLand() -> Bool {
return true
}
func hasWarmBlooded() -> Bool {
return true
}
}
struct Cat : Animal , Mammal {
func speaks {
print("Meo Mew")
}
}
---------------------------------------------------
let cat = Cat()
cat.speaks() //Meow Meow
cat.waksOnNumberOfLegs() // 4
cat.walksOnLand() //true
cat.hasWarmBlooded() //true

As you can see we can conform to methods of various different protocols and access those features. Unlike normal subclassing where we have to define one parent superclass, in POP we can adhere to various interfaces/protocols and call their implementation using dynamic dispatch at runtime. This is called as retroactive modeling of classes.

Now let's see another use case where we have to define food for different animals, say Cow and Dog. Cow eats grass and dog eats Bone. So in inheritance approach we may try to do it like this:

We have a generic method in the Animal Base class which takes generic food:

func eat(food: Food) {
}

Each Animal type can define there own food by overriding the default implementation and pass there own respective food type but the compiler won’t understand the type of Food as it will mismatch.

We can overcome this upfront modeling and exclusive hierarchy issue with the help of Protocol with associated types.

Protocol with Associated Types

Associated types are a powerful way of making protocols generic. Generic enables you to write flexible, reusable functions and types that can work with any type, subject to requirements that you define.

You can write code that avoids duplication and expresses its intent in a clear, abstracted manner. Here with generic Protocol type we can define any generic food type which can even:

protocol Food { }class Grass : Food { }
class Bone : Food { }
protocol Animal {
associatedtype F
func eat(f:F)
}
class Cow<T> : Animal {
func eat(f:T) {
print(f)
}
}
class Dog<T> : Animal {
func eat(f:T) {
print(f)
}
}
----------------------------------
let dog = Dog<Bone>()
dog.eat(f: Bone()) // Bone
let cow = Cow<Grass>()
cow.eat(f:Grass()) // Grass
let vegetarianDog = Dog<Grass>()
dog.eat(f:Grass()) //Grass

As you can see we don’t need to upfront define the Food type of a Animal in POP. It can be any type and it can be dynamically injected at runtime also as its generic. We can have vegetarian dogs eating grass and we don’t need to make any new changes as we can define food type for any new animal type as it's not strongly coupled with any base interface and even new food type can be added at runtime using ad hoc hierarchies and retroactive modeling.

Our cute Zombie Dog

Say there is a virus outbreak and all Dogs turned into zombies. A Zombie dog came across a cow who is eating grass, the Zombie Dog wants to eat the cow. So using this POP architecture we can have a new food type passed as Generic to our Animal type.

class Dog<T> : Animal { typealias AnyFood = T // You can rename the generic type for   better readability using typealias. func eat(f:AnyFood) {
print(f)
}
}
let zombiedog = Dog<Cow<Grass>>()
zombiedog.eat(f: Cow()) //Cow

This makes our code very flexible and loosely coupled following SOLID principles.

So moving ahead try to make a practice to use more and more protocol-oriented programming and use its features to improve and establish a cleaner and scalable architecture.

Thanks for reading!

Few Good Reads: https://matteomanferdini.com/protocol-oriented-programming/#section1

Follow me for more advanced topics I will release in 2020.

Please leave some multiple claps if you like the article.
Follow me to access more advanced topics in 2020.

--

--

Mushtaque Ahmed

Mobile Architect | iOS | Swift | RxSwift | GRPC | VIPER | | Protocol Oriented Programming | Realm | cocoapods | Appium | HTML | React.js | React Native