SOLID: Dependency Inversion Principle(DIP)

Mohshin Shah
2 min readFeb 8, 2021

--

Robert C. Martin’s defined the of the DIP as below with two parts

High-level modules should not depend on low-level modules. Both should depend on abstractions.

Abstractions should not depend on details. Details should depend on abstractions.

What is High-level and Low-level?

High level meaning the business requirement and low-level means the technical details, how that is implemented. For example Payment Processing can called higher level of module and how it is done technically using Visa’s framework, Internet banking etc are the lower level details.

Here is one more example, Loading the feed depending on the Network which is a low level module and if it is to be changed then FeedLoader will break. For example for writing Unit test cases we need Mock network or use of some third party network library instead this cases will break the higher level module Feed.

class Network {
func request(_ urlRequest: URLRequest, completion: @escaping (Result<Data, Error>) -> Void) {
...
URLSession.shared.request. bla bla
}
}
class FeedLoader {
let network: Network
init(_ network: Network) {
self.network = network
}
func loadFeed(url: URLRequest, completion: @escaping (Result<[String], Error>) -> Void) {
// Loading Feed using URLSession
network.request
}
}

In the example above,

Feed Module (High Level) depending on the Low-Level Network Module. So to solve we have to introduce one layer of abstraction

to make higher level and low level implementation both depend on the interface/abstraction layer

Feed Module — — — — — → Abstraction ← — — — — Network

protocol Network {
func request(_ urlRequest: URLRequest, completion: @escaping (Result<Data, Error>) -> Void)
}
class URLNetwork: Network {
func request(_ urlRequest: URLRequest, completion: @escaping (Result<Data, Error>) -> Void) {
URLSession.shared...
}
}class AlamofireNetwork: Network {
func request(_ urlRequest: URLRequest, completion: @escaping (Result<Data, Error>) -> Void) {
AF.shared...
}
}class MockNetwork: Network {
func request(_ urlRequest: URLRequest, completion: @escaping (Result<Data, Error>) -> Void) {
let dummyDataFortesting = ....
completion(.success(dummyDataFortesting))
}
}
class FeedLoader {
let network: Network
init(_ network: Network) {
self.network = network
}
func loadFeed(url: URLRequest, completion: @escaping (Result<[String], Error>) -> Void) {
// Loading Feed using URLSession
network.instance.
}
}

Now we can test, change lower-level framework without affecting the Feed module.

As mentioned in earlier post, All SOLID principles are intertwined with each other, So If we apply OCP and LSV properly we would automatically achieve the DIP.

That’s all for this time. Thanks for reading my articles. Cheers 🍺🍺🍺

--

--