The Liskov Substitution Principle and Swift

Hello ladies and gentlemen, Leo here. Today’s subject of study is the Liskov Substitution Principle implementation on Swift and why that is important to you as an iOS developer.

First, we have to say that the Liskov principle was created by Barbara Liskov. She is an amazing woman who is one of the first women in the United States to achieve a Doctorate in Computer Science and is a Turing Award winner. For more information her wikipedia’s article. You may find more at her webpage.

And you are right today we will talk about the L in SOLID principles.

 

The Liskov Substitution Principle – The Theory and Code Setup

 

The Liskov Substitution Principle is described by:

Substitutability is a principle in object-oriented programming stating that, in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e. an object of type T may be substituted with any object of a subtype S) without altering any of the desirable properties of the program (correctness, task performed, etc.).

So let’s see how Swift implements this principle.
Let’s code!

protocol Food {} // 1
extension Food {
    func display() { print("Yeap it's food") }
}

protocol Fruit: Food {} //2
extension Fruit {
    func display() { print("Now, its fruit..") }
}

struct FingerFood: Food {} //3
struct FingerFoodFruit: Fruit{} // This is a Fruit and a Food type.

First, we are creating two protocols Food and Fruit. Fruit inherits all the Food capabilities, in this example the display function. So and also overwrite the Food’s function display giving it a new implementation. And at the final part, we have two structs that extend the Food and the Fruit protocol.

If you run the struct’s methods the results would be:

FingerFood().display()
FingerFoodFruit().display()

Resulting in:

 

Applying Liskov Substitution Principle in our example

The FingerFoodFruit is at the same time the type of food and the type of Fruit. This way we can use the Liskov principle to pass it to a function that receives Food like the code below:

let classicFood = FingerFood()
let apple = FingerFoodFruit()

func setFood(_ food: Food) { // 1
    print("food loaded")
}

setFood(classicFood)
setFood(apple)

Result in:

Now we start to use the Liskov principle. We are passing the apple which is the Fruit type but because it’s inherited the super-type of Food protocol too we can pass it to the method.

Now let’s see something more interesting. What if we set a variable of type Food but assign to it a real type Fruit object, what the display method will print?

let strangeFood : Food = FingerFoodFruit() // an food type variable with a Fruit object inside...
strangeFood.display() // what????

And you can see in the console the message below:

Why is that? The Swift compiler can only guarantee the display method of the TYPE of the VARIABLE, this way we can assure the type-safe and everybody goes happy home…

But wait, what if we wanted to access the display method of the child rather than the super-type display method. Well in Swift we have some ways to do that and all include the **as** reserved word. Check below:

 

let strangeFood : Food = FingerFoodFruit() // a Food variable with a Fruit Object inside...
(strangeFood as Fruit).display() // 1 -this doesn't work... but why? 
(strangeFood as! Fruit).display() // 2 -this works...
(strangeFood as? Fruit)?.display() // 3- this works too...

Commenting on the line that doesn’t work we have this output:

The number 1 doesn’t work because the **as** operator checks in *compile-time* if the variable is convertible to the type we are trying to. At the time I’m writing this article Swift doesn’t have an automatic downcast in compile time. But as you can see you can *force downcast* in runtime to the type you are trying to work to, this way we have access to the Fruit behavior of the display method.

And you are asking yourself… If I can force downcast, can I upcast the fruit variable to access the super-type display method behavior?
Yes, you can, and all thanks to the Liskov Principle.

let apple = FingerFoodFruit() // its a Fruit variable with a Fruit object inside...
(apple as Food).display() // now this is safe in compile-time because all Fruit types are also Food types

And the output:

And now, we are done!

 

Summary

Well, this was a little introduction to the Barbara Liskov Substitution Principle and I hope you have enjoyed discovering these language tricks as I did. And please go check other Barbara’s works she is an amazing scientist.

That’s all my people, I hope you liked reading this article as much as I enjoyed writing it. If you want to support this blog you can Buy Me a Coffee or leave a comment saying hello. You can also sponsor posts and I’m open to freelance writing! You can reach me on LinkedIn or Twitter and send me an e-mail through the contact page.

Thanks for the reading and… That’s all folks.

Credits:

ps: and yes, it’s all about the L in the legendary SOLID acronym.
Cover image credit: Cody O’Loughlin / Quanta Magazine.

 

Share this post:

Related posts

Sponsor