Interfaces vs Inheritance in Swift

ansu jain
3 min readMar 21, 2017

When creating software, it’s useful to understand a wide range of design principles. Understanding how to design a system with the most appropriate principle can save countless hours of development and headache.

Let’s first talk about inheritance for a bit. Inheritance is when a class inherits state and/or behavior from a parent class. Let’s say that we are designing a game, and I need a Dog:

class Dog {
func bark(){
print("Bark")
}
}

After a while, we realize that our software, like everything, needs Cats, so we create a Cat class:

class Cat{
func .meow(){
print("Meow!")
}
}

Because nature calls, we add .poop() to the Cat and the Dog class:

class Dog {
func bark(){
print("Bark")
}
func poop(){
print("Poop")
}
}
class cat{
func meow(){
print("Meow")
}
func poop(){
print("Poop")
}
}

In this example, we have two animals those are able to poop. Unfortunately, they both provide implementations for poop(), so there’s some code duplication here. so we lift .poop() into a shared Animal class.

Animal
.poop()
Dog
.bark()
Cat
.meow()

Now that we have a lot of animals pooping everywhere, we need a cleaningrobot:

CleaningRobot
.drive()
.clean()

You also need a MurderRobot that can .drive() and .kill() the Cats and Dogs that are .poop()ing all over your white floors:

MurderRobot
.drive()
.kill()

Since .drive() is now duplicated between CleaningRobot and MurderRobot we create a Robot class to put it in.

Robot
.drive()
CleaningRobot
.clean()
MurderRobot
.kill()

This is what the whole structure looks like:

Robot
.drive()
CleaningRobot
.clean()
MurderRobot
.kill()
Animal
.poop()
Dog
.bark()
Cat
.meow()

“Our customers demand a MurderRobotDog. It needs to be able to .kill(), .drive(), .bark(), but it cannot poop().

And now, we’re screwed. We simply cannot fit the MurderRobotDog nicely into this inheritance hierarchy. We could create a new parent object, where you put all the functionality that is shared:

GameObject
.bark()
Robot
.drive()
CleaningRobot
.clean()
MurderRobot
.kill()
MurderRobotDogAnimal
.poop()
DogCat
.meow()

But that means that your objects will have a ton of functionality that they don’t use, so you end up with a Gorilla/Banana problem — you request a banana, but you end up with a gorilla holding the banana and the entire jungle with it.

We can, however, model this with protocols in Swift.How can protocols provide a better abstraction?

A protocol in Swift defines methods or properties that a class can then adopt. Here is an example:

protocol Barker {
func bark()
}
protocol Pooper {
func poop()
}
protocol Driver {
func drive()
}
protocol Cleaner {
func clean()
}
protocol Killer {
func kill()
}

Since Classes can adopt multiple protocols. The MurderRobotDog class adopt the Barker, Killer and driverprotocol, meaning MurderRobotDog class provide implementations for bark(), kill(), and clean().

class MurderRobotDog: Barker,Killer, Driver{
func bark() {
print("Bark!")
}
func driver() {
print("Drive!")
}
func killer() {
print("Kill!")
}
}

As of Swift 2.0, we can now remove the code duplication by providing a default implementation using a protocol extension:

protocol Barker {
func bark()
}

extension Barker {
func bark() {
print("Bark!")
}
}
class Dog: Barker{}
let myDog = Dog()
myDog.bark() // prints “Bark!”

So we’ve looked at an example of an inheritance tree that broke down, and then we looked at how to restructure it using protocol(interface).

The question that is probably on your mind now is — when to use each one? Well… the vast majority of developers agree that we should favor Interface over inheritance. A lot of people are going to tell you that if something has a “is a” relationship, then you should use inheritance. For example, Mattias “is a” man, thus I can inherit man. If the relationship is of a “has a” nature, such as a car “has an” engine, then you should use composition.

Conclusion

When designing a system, it’s important to pick the right design principle for your model. In many circumstances, it’s just better to use interface in the first place. It’s more flexible, powerful, and it’s also very easy to do.

--

--