SOLID Principles (Series) [PART 4]
SOLID - make Object-Oriented designs more understandable, flexible and maintainable.
![SOLID Principles (Series) [PART 4]](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1666673548073%2FH3QdkOhn-.jpg&w=3840&q=75)
Senior Android Engineer from Bangladesh. Love to contribute in Open-Source. Indie Music Producer.
Search for a command to run...
SOLID - make Object-Oriented designs more understandable, flexible and maintainable.
![SOLID Principles (Series) [PART 4]](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1666673548073%2FH3QdkOhn-.jpg&w=3840&q=75)
Senior Android Engineer from Bangladesh. Love to contribute in Open-Source. Indie Music Producer.
No comments yet. Be the first to comment.
In this series SOLID Principles with real life example. Happy learning...
SOLID - make Object-Oriented designs more understandable, flexible and maintainable.
A practical guide to choosing the right coroutine primitive for your Android/Kotlin projects

This guide explains what Channels are, when to use them, how to use them correctly, and when NOT to use them β in simple, professional language. 1. What problem do Channels solve? In Kotlin coroutines, you often have multiple coroutines running at t...

Learn how to build preview-safe ViewModels in Jetpack Compose using Hilt.

HandlerInterceptor vs Filter in Spring Boot: A Practical Guide with JNDI Injection Prevention

Simple In-Memory Caching: A Tiny Trick with Massive Impact

If get to know something new by reading my articles, don't forget to endorse me on LinkedIn
Segregation means keeping things separated, and the Interface Segregation Principle is about separating the Interfaces.
The principle states that many client-specific interfaces are better than one general-purpose interface.
Clients should not be forced to implement a function they do no need.
ISP splits interfaces that are very large into smaller and more specific ones so that clients will only have to know about the functions that are of interest to them.
Donβt depend on things you donβt need. Interfaces containing methods that are not specific to it are called polluted or fat interfaces. We should avoid them.
Many client-specific interfaces are better than one general-purpose interface. When we have non-cohesive interfaces, the ISP guides us to create multiple, smaller, cohesive interfaces.
Let's continue with the example from LSP.
interface MediaPlayer {
fun playAudio(mediaContent : MediaContent)
}
interface VideoMediaPlayer : MediaPlayer {
fun playVideo(mediaContent : MediaContent)
}
class VLCMediaPlayer : VideoMediaPlayer {
@override fun playAudio(mediaContent : MediaContent) {...}
@override fun playVideo(mediaContent : MediaContent) {...}
}
class WinampMediaPlayer : MediaPlayer {
@override fun playAudio(mediaContent : MediaContent) {...}
}
Above example is okay by following LSP. But what if we add another media player called VideoOnlyMediaPlayer?.
class VideoOnlyMediaPlayer : VideoMediaPlayer {
@override fun playAudio(mediaContent : MediaContent) {...}
@override fun playVideo(mediaContent : MediaContent) {...}
}
And, That's where the problem occurs. VideoOnlyMediaPlayer is forced to implement playAudio() function.
New MediaPlayer called VideoOnlyMediaPlayer should support only video MediaContent. It was not designed to play audio MediaContent. Since VideoMediaPlayer extends MediaPlayer, all Classes who implement VideoMediaPlayer must implement the playAudio() function.
Which state that We're forcing client to implement unnecessary functions.
So, how do we solve this problem by following ISP?.
We can modify our VideoMediaPlayer not to be extended from MediaPlayer. In this case, clients who doesn't need support for audio MediaContent won't be forced to implement playAudio() function.
Clients who needs support for both audio & video MediaContent must implements MediaPlayer and VideoMediaPlayer at their own.
Let's get into the solution.
interface AudioMediaPlayer {
fun playAudio(mediaContent : MediaContent)
}
interface VideoMediaPlayer {
fun playVideo(mediaContent : MediaContent)
}
class VLCMediaPlayer : VideoMediaPlayer, AudioMediaPlayer {
@override fun playAudio(mediaContent : MediaContent) {...}
@override fun playVideo(mediaContent : MediaContent) {...}
}
class WinampMediaPlayer : AudioMediaPlayer {
@override fun playAudio(mediaContent : MediaContent) {...}
}
class VideoOnlyMediaPlayer : VideoMediaPlayer {
@override fun playVideo(mediaContent : MediaContent) {...}
}
So, what's the changes here?
AudioMediaPlayer and VideoMediaPlayer is now independent Interface, there is no dependency between them. [MediaPlayer refactored to AudioMediaPlayer]
New introduced VideoOnlyMediaPlayer supports video MediaContent only. Thus, VideoOnlyMediaPlayer is not forced to implmenet playAudio() function as there is no inter-connection between AudioMediaPlayer and VideoMediaPlayer.
VideoOnlyMediaPlayer support video MediaContent only. VLCMediaPlayer support both audio & video MediaContent. WinampMediaPlayer support only audio MediaContent as we have expected.
The principle states that many client-specific interfaces are better than one general-purpose interface.
Clients should not be forced to implement a function they do no need.
ISP splits interfaces that are very large into smaller and more specific ones so that clients will only have to know about the functions that are of interest to them.
In the next & last article of this series, we will talk about Dependency Inversion Principle.
That's it for today. Happy Coding...