SOLID Principles (Series) [PART 4]

SOLID Principles (Series) [PART 4]

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

If get to know something new by reading my articles, don't forget to endorse me on LinkedIn

What is ISP?

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.

Things to remember

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

Again what's the problem with this design from LSP?

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.

Recap

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