SOLID Principles with Examples in iOS & Swift (Part 2)

ansu jain
The Create School
Published in
4 min readSep 10, 2017

--

In Part 1, I have explained top two principles (Single Responsibility and open/closed). Here is the link, if you would like to learn:

In this part, I will try to explain Liskov Substitution and Interface Segregation Principle.

Liskov Substitution Principle:

The Liskov Substitution Principle (LSP) states that objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program. What that means is that when you inherit from a class or an abstract class or implement an interface (protocol), your objects should be replaceable and injectable wherever that interface or class that you subclassed from was used. This principle is often referred to as design by contract or, as of late in the Swift community, referred to as protocol-oriented programming. The main message of this principle is that you should not violate the contract that your interfaces that you subclass from promise to fulfill and that by subclassing, those subclasses could be used anywhere where the superclass was previously used. For example, If we have PostsStorage class, then according to Liskov’s Substitution Principle we could say that if we subclass from it, let’s call it BetterPostsStorage, then everywhere we were using the original PostsStorage, we could be using BetterPostsStorage instead and our app won’t break or misbehave in any way.

Interface Segregation Principle:

The Interface Segregation Principle (ISP) says many client-specific interfaces are better than one general-purpose interface. It also states that no client should be forced to depend on and implemented methods it does not use. What that means is that when you create interfaces (protocols) that your classes implement, you should strive for and depend on abstraction over specificity but not until it becomes a waste where you have to implement a bunch of methods your new class doesn’t even use.

If you want to know the difference between interface and inheritance, request you to go through this article first : https://medium.com/@ansujain/interfaces-vs-inheritance-in-swift-1f48c85948b8

For a lack of a better (shorter) example, let’s pretend that we have the following classes and interfaces:

protocol WorkerInterface {
func eat()
func work()
}
class Worker: WorkerInterface { func eat() {
print("worker's eating lunch")
}
func work() {
print("worker's working")
}
}
class Contractor: WorkerInterface {
func eat() {
print("contractor's eating lunch")
}
func work() {
print("contractor's working")
}
}
class Manager {
private let workers: [WorkerInterface]
init(workers: [WorkerInterface]) {
self.workers = workers
}

func manage() {
workers.forEach { (worker: WorkerInterface) in
worker.work()
}
}
}
let worker1 = Worker()
let worker2 = Worker()
let contractor = Contractor()
let manager = Manager(workers: [worker1, worker2, contractor])
manager.manage()

Here we have a WorkerInterface that has two methods eat() and work(). And we have two classes that implement it: Worker and Contractor. And we have a Manager that relies on WorkerInterface to call work() on each one of the passed worker and contractor objects to initiate the work. It’s all nice and good and we are assuming here that all workers and contractors are humans who can work but also need to eat so implementing the eat() method in both of them is perfectly reasonable (we are assuming that the eat() method is called on those objects somewhere else in the application).

But this quickly becomes unreasonable when we introduce a Robot class that complies to the same WorkerInterface:

class Robot: WorkerInterface {
// do nothing here. cuz robots don’t eat.
func eat() {}

func work() {
print("robot's working")
}
}

This violates ISP because our Robots don’t need to eat and Robot is forced to implement an interface it doesn’t fully need, hence an empty eat() method.

What we need instead is to extract better, more concrete interfaces and use them instead:

protocol WorkableInterface {
func work()
}
protocol FeedableInterface {
func eat()
}
class Worker: WorkableInterface, FeedableInterface {
func eat() {
print("worker's eating lunch")
}
func work() {
print("worker's working")
}
}
class Robot: WorkableInterface {
func work() {
print("robot's working")
}
}

Now we rely on a more specific WorkableInterface that Manager uses, and Robot doesn’t have to implement what it doesn’t need. This in a nutshell is what the Interface Segregation Principle is all about. You have to either do a little bit more design up front to get your interfaces/protocols right or you resolve it with adapters in existing systems. But ISP helps you maintain Liskov’s Substitution Principle in your code as well.

That’s it for today, I hope it will be useful for you. Please let me know your thoughts and opinions :). If you enjoyed it, feel free to hit the clap button below 👏 to help others find it! In next part, I will write Dependency inversion principle.

--

--