Romman Sabbir
Romman Sabbir | Official

Romman Sabbir | Official

SOLID Principles (Series) [PART 5]

SOLID Principles (Series) [PART 5]

SOLID - make Object-Oriented designs more understandable, flexible and maintainable.

Romman Sabbir's photo
Romman Sabbir
·Oct 29, 2022·

2 min read

Subscribe to my newsletter and never miss my upcoming articles

Play this article

Table of contents

  • What is DIP?
  • Things to remember
  • Example
  • Recap

Previous articles on SOLID Principles

What is DIP?

DIP (Dependency Inversion Principle) states that:

  • 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.

Things to remember

  • Design by contract.

  • Every dependency in the design should target an interface or an abstract class. No dependency should target a concrete class.

  • Factories and Abstract Factories can be used as dependency frameworks, but there are specialized frameworks for that such as Spring IOC (Inversion of Control Container).

Let's take an simple Songs Repository example. Where Data Source could be Remote or Local. When device is connected to the internet, data should be fetched from Remote, else it should return whatever has been Cached to the Local.

Example

interface SongsRepository {
    fun searchSongs(query : String) : MutableList<SongObject>
}
class SongsRepositoryImpl constructor (private val dataSource : DataSource)  : SongsRepository {
    @override fun searchSongs(query : String) : MutableList<SongObject> {...}
}

Here, the implementation depends on abstraction (DataSource interface) that can be injected through its constructor.

Let's create the DataSource interface with two implementations of DataSource.

interface DataSource {
    fun createConnection(config : DataSourceConfig) : Boolean
    fun getConnection() : DataSourceConnection
}
interface RemoteDataSource {
    @override fun createConnection(config : DataSourceConfig) : Boolean {...}
    @override fun getConnection() : DataSourceConnection {...}
}
interface LocalDataSource {
    @override fun createConnection(config : DataSourceConfig) : Boolean {...}
    @override fun getConnection() : DataSourceConnection {...}
}

Based on device internet connectivity, we can switch from RemoteDataSource to LocalDataSource (when device is not connected to the internet) then we only need to pass this object from the client.

What's has been done through the example?

  • SongsRepositoryImpl class depends on the DataSource interface, not the concrete class.

  • Now due to abstraction, Client can change implementation at any time. For example, when device is connected to the internet, client can pass an instance of RemoteDataSource else LocalDataSource.

Recap

  • 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.

That's it for this series. Happy Coding...

 
Share this