The Swift Blog: OOP vs. POP

There’s a lot of controversy about two programming patterns, the old and very widely used Object-Oriented Programming, and the new and rebellious Protocol-Oriented Programming. But what are those anyway, and how do they affect your code, if at all? Well, you’ve come to the right place because I’m going to answer these questions today.

OOP (Object-Oriented Programming)

In OOP every custom type we deal with is an object, in other words a class. The idea here is that you always start with a base class, and then subclass this as needed. To show you this in action here an example:

class Animal {}

This Animal class does nothing in particular, but will be our base class for all animals we need in our app. So, let’s create a couple more animals to show you how that would look like.

class Dog: Animal {
  func bark() {}
}

class Cat: Animal {
  func meow() {}
}

Hm, not that useful, is it? Well, let’s change our base class to make it a little more useful, like so:

class Animal {
  let numberOfLegs: Int

  init(numberOfLegs: Int) {
    self.numberOfLegs = numberOfLegs
  }

  func makeNoise() {}
}

And change our animals to reflect this change, like so:

class Dog: Animal {
  let hasFur: Bool

  init(hasFur: Bool) {
    self.hasFur = hasFur
    super.init(numberOfLegs: 4)
  }

  override func makeNoise() {
    print("bark")
  }
}

class Cat: Animal {
  let hasFur: Bool

  init(hasFur: Bool) {
    self.hasFur = hasFur
    super.init(numberOfLegs: 4)
  }

  override func makeNoise() {
    print("meow")
  }
}

That looks better and shows you exactly what a subclass does. It inherits all properties and functions of its’ superclass, in this case the initializer and the makeNoise function. Hence the term “Inheritance”, which is often used when people refer to OOP. And what is that call to super.init? Well, because we’re inheriting we always need to call the superclass’s initializer after initializing all other values.

POP (Protocol-Oriented Programming)

POP was unveiled by Apple in 2015 during WWDC. Other than OOP, you don’t use classes and inheritance to build your model objects, but instead make use of structs and protocol composition. Let’s take the same example from above and convert it to POP:

protocol Animal {
  var hasFur: Bool { get }
  func makeNoise()
}

This declares a protocol named Animal with two requirements, and that is that every type conforming to this protocol has to add the function makeNoise and the property hasFur.

struct Dog: Animal {
  let hasFur: Bool

  func makeNoise() {
    print("bark")
  }
}

struct Cat: Animal {
  let hasFur: Bool

  func makeNoise() {
    print("meow")
  }
}

Doesn’t this already look much better? No custom initializers, no call to super. All we do here is conform to rules which are provided by the protocol.

Composition vs. Inheritance

There are a couple of problems which might occur should you decide to go with class inheritance. First of all you need to mark every property and function you inherit with the override keyword. This is not the worst thing to do, but it’s kinda ugly. But the real dealbreaker, at least for me, is that you can’t inherit more than one class. Let’s take a look at another example:

class Animal {}
class Quadruped {}
class Dog: Animal, Quadruped {}

In this case it doesn’t really matter what those classes do because the code above won’t even compile. If you try this in a playground you will get an error saying: “Multiple inheritance from classes ‘Animal’ and ‘QuadruPed’”. Would this work using protocols? Let’s see:

protocol Animal {}
protocol Quadruped {}
struct Dog: Animal, Quadruped {}

This code will compile just fine, because classes and structs can conform to multiple protocols without any problem. You can take this even further and create a typealias or protocol that acts as a wrapper around those two protocols like so:

typealias FourLeggedAnimal = Animal & Quadruped
OR
protocol FourLeggedAnimal: Animal, Quadruped {}

Something like this wouldn’t be possible with class inheritance. Instead you would need to subclass Quadruped to be a a subclass of Animal. But what if you need another type of animal, like a biped? Well, this would need to subclass Animal as well, which if you’re not careful can become a pretty big mess. Why all this fuss about it? Protocols allow us to split up requirements into multiple small parts, which we can then conform to as we need.

protocol Animal {
  var numberOfLegs: Int { get }
}

protocol Quadruped {
  var hasFur: Bool { get }
}

protocol Biped {
  var canFly: Bool { get }
}

In this case Dog can be an Animal and a Quadruped, whereas a Duck might conform to Animal and Biped.

Composition over Inheritance

Apple made it pretty clear that we, if we have the choice, should always choose protocol composition over class inheritance. Creating multiple small protocols and conforming to only those you need, is a lot easier than building complex class hierarchies which rely on each other, and in the worst case break your entire project should you change one of those classes. Should you stop using classes and inheritance altogether now? Well, no. Classes still have their use cases, and much of Apple’s APIs rely on classes and inheritance.

Conclusion

While POP is pretty new and Apple’s APIs rely heavily on classes and inheritance it might make sense to give your own custom types a special treatment and make them conform to POP. Not only will you make your code easier to reason about, you will also introduce a new level of safety into your projects with structs and protocol composition. And while we weren’t able to serialize structs to disk for a long time, the Codable protocol in combination with NSCoding make this almost trivial.