SOLID Principles (Series) [PART 3]
SOLID - make Object-Oriented designs more understandable, flexible and maintainable.
![SOLID Principles (Series) [PART 3]](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1665554558214%2FICBbB8Zoe.jpg&w=3840&q=75)
Senior Android Engineer from Bangladesh. Love to contribute in Open-Source. Indie Music Producer.
If get to know something new by reading my articles, don't forget to endorse me on LinkedIn
In the last article, we talked about Open-Closed Principle which is one of the key concepts in OOP that enables us to write robust, maintainable and reusable software components. But following the rules of that principle alone is not enough to ensure that you can change one part of your system without breaking other parts. Our classes and interfaces also need to follow the Liskov Substitution Principle to avoid any side-effects. LSP extends OCP by focusing on the behavior of a superclass and its subtypes.
What is LSP?
LSP defines that objects of a superclass shall be replaceable with objects of its subclasses without breaking the application. That requires the objects of your subclasses to behave in the same way as the objects of your superclass.
Let's simplify this the scenario.
This principle applies to inheritance hierarchies and is just an extension of the Open Close Principle.
It means that we must make sure that new derived classes are extending the base classes without changing their original behavior. Basically, derived classes should never do less than their base class.
If a subtype of the supertype does something that the client of the supertype does not expect, then this is in violation of LSP. Imagine a derived class throwing an
Exceptionthat the superclass does not throw, or if a derived class has some unexpected side effects. One has to consider that how the client programs are using the class hierarchy. Sometimes code refactoring is required to fix identified LSP violations.
Example: Bad Design and Implementation
Let's think in-terms of a MediaPlayer
interface MediaPlayer {
fun playAudio(mediaContent : MediaContent)
fun playVideo(mediaContent : MediaContent)
}
Let's work on two media player VLC Media Player and Winamp Media Player.
[Note: Winamp doesn't support Video]
class VLCMediaPlayer : MediaPlayer {
@override fun playAudio(mediaContent : MediaContent) {...}
@override fun playVideo(mediaContent : MediaContent) {...}
}
class WinampMediaPlayer : MediaPlayer {
@override fun playAudio(mediaContent : MediaContent) {...}
@override fun playVideo(mediaContent : MediaContent) {
throw VideoNotSupportedException()
}
}
class VideoNotSupportedException(@override val message = "Winamp doesn't support Video") : RuntimeException() {}
Everything looks good and okay, right? No!. Why? Let's find out.
When we pass a audio
MediaContentto theMediaPlayer,MediaPlayerwill try to play the audioMediaContent. BothVLCMediaPlayerandWinampMediaPlayersupport audio playing functionality.But when we a video
MediaContentto theMediaPlayer,MediaPlayerwill try to play the videoMediaContent.VLCMediaPlayerwill be able to play the videoMediaContentbut when it's aboutWinampMediaPlayerit will throwVideoNotSupportedException.That's where LSP is violated in
WinampMediaPlayer, becuase it's breaks the original behavior of the super classMediaPlayer.
So, how do we solve this "breaking"?
Example: Good Design and Implementation
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) {...}
}
So, what's the changes here?
Previous
MediaPlayerfunctionality has been divided into two part now, whereMediaPlayeris responsible to play audioMediaContentandVideoMediaPlayer(extendMediaPlayer) is responsible to play both audio and videoMediaContent.VLCMediaPlayersupports audio and videoMediaContentas expected andWinampMediaPlayersupports only audioMediaContent.Now, here LSP is not violated in
WinampMediaPlayer, becuase it's doesnt breaks the original behavior of the super classMediaPlayer.
Recap
LSP defines that objects of a superclass shall be replaceable with objects of its subclasses without breaking the application. That requires the objects of your subclasses to behave in the same way as the objects of your superclass.
In the next article, we will talk about Interface Segregation Principle.
That's it for today. Happy Coding...




