# SOLID Principles (Series) [PART 3]

> If get to know something new by reading my articles, don't forget to endorse me on [LinkedIn](https://www.linkedin.com/in/rommansabbir/)

In the last [article](https://rommansabbir.com/solid-part-2), 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](https://rommansabbir.com/solid-part-2).
    
* 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 `Exception` that 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`

```kotlin
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\]

```kotlin
class VLCMediaPlayer : MediaPlayer {
    @override fun playAudio(mediaContent : MediaContent) {...}
    @override fun playVideo(mediaContent : MediaContent) {...}
}
```

```kotlin
class WinampMediaPlayer : MediaPlayer {
    @override fun playAudio(mediaContent : MediaContent) {...}
    @override fun playVideo(mediaContent : MediaContent) {
        throw VideoNotSupportedException()
    }
}
```

```kotlin
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 `MediaContent` to the `MediaPlayer`, `MediaPlayer` will try to play the audio `MediaContent`. Both `VLCMediaPlayer` and `WinampMediaPlayer` support audio playing functionality.
    
* But when we a video `MediaContent` to the `MediaPlayer`, `MediaPlayer` will try to play the video `MediaContent`. `VLCMediaPlayer` will be able to play the video `MediaContent` but when it's about `WinampMediaPlayer` it will throw `VideoNotSupportedException`.
    
* That's where **LSP** is **violated** in `WinampMediaPlayer`, becuase it's **breaks** the original behavior of the super class `MediaPlayer`.
    

So, how do we solve this "**breaking**"?

### Example: Good Design and Implementation

```kotlin
interface MediaPlayer {
    fun playAudio(mediaContent : MediaContent)
}
```

```kotlin
interface VideoMediaPlayer : MediaPlayer {
    fun playVideo(mediaContent : MediaContent)
}
```

```kotlin
class VLCMediaPlayer : VideoMediaPlayer {
    @override fun playAudio(mediaContent : MediaContent) {...}
    @override fun playVideo(mediaContent : MediaContent) {...}
}
```

```kotlin
class WinampMediaPlayer : MediaPlayer {
    @override fun playAudio(mediaContent : MediaContent) {...}
}
```

So, what's the changes here?

* Previous `MediaPlayer` functionality has been divided into two part now, where `MediaPlayer` is responsible to play audio `MediaContent` and `VideoMediaPlayer` (extend `MediaPlayer`) is responsible to play both audio and video `MediaContent`.
    
* `VLCMediaPlayer` supports audio and video `MediaContent` as expected and `WinampMediaPlayer` supports only audio `MediaContent`.
    
* Now, here **LSP** is not **violated** in `WinampMediaPlayer`, becuase it's doesnt **breaks** the original behavior of the super class `MediaPlayer`.
    

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