- Link: The Interface Segregation Principle
- Author: Escape Velocity Labs
- Publication date: July 30, 2023

In this series of articles you will become familiar with the SOLID principles, which will help you write more modular, understandable, and maintainable code. SOLID is an acronym that encompasses the following principles:
- Single Responsibility Principle
- Open/Closed Principle
- Liskov Substitution Principle
- Interface Segregation Principle
- Dependency Inversion Principle
In this article we will explore the fourth of these principles, called the Interface Segregation Principle. This principle states that no code should depend on functionality it does not use.
To learn Python and build the skills to land your first job, check out our Professional Python Developer Bootcamp.
How to apply the Interface Segregation Principle
Let’s continue with the example we saw in the previous article, where we implemented a music streaming application. We will be able to use this app on different types of devices: web browser, desktop application, and phone application.
Our app, on each of these devices, will be able to perform some functions but not others. For example, only the desktop and phone apps can download songs (and also delete downloaded songs). However, the web browser cannot do this.
On the other hand, the web browser can display song lyrics in its interface (and hide them too), but the other devices cannot.
One way to implement this would be to define a common interface, called MusicPlayer, and then, on each device create a specific player that implements those methods in an appropriate way for the platform:
from abc import ABC, abstractmethod
class MusicPlayer(ABC):
@abstractmethod
def play_song(self):
pass
@abstractmethod
def stop_song(self):
pass
@abstractmethod
def download_song(self):
pass
@abstractmethod
def delete_song(self):
pass
@abstractmethod
def show_lyrics(self):
pass
@abstractmethod
def hide_lyrics(self):
pass
class WebPlayer(MusicPlayerInterface):
def play_song(self):
print("Playing song on web player")
def stop_song(self):
print("Stopping song on web player")
def show_lyrics(self):
print("Showing lyrics on web player")
def hide_lyrics(self):
print("Hiding lyrics on web player")
def download_song(self):
pass
def delete_song(self):
pass
class MobilePlayer(MusicPlayer):
# Implement the appropriate methods. 'pass' on the others.
class DesktopPlayer(MusicPlayer):
# Implement the appropriate methods. 'pass' on the others.
However, this implementation forces your colleagues working on the different platforms to deal with code that is irrelevant to them. Additionally, as new devices emerge and you have to extend the functionality of this class, you will have to force the previous platforms to include that code as well. This makes both development and testing, as well as understanding your code, more difficult.
To solve this, we are going to segregate our music player interface into different smaller interfaces according to the functionality they fulfill:
from abc import ABC, abstractmethod
class Playable(ABC):
@abstractmethod
def play_song(self):
pass
@abstractmethod
def stop_song(self):
pass
class Downloadable(ABC):
@abstractmethod
def download_song(self):
pass
@abstractmethod
def delete_song(self):
pass
class LyricsDisplayable(ABC):
@abstractmethod
def show_lyrics(self):
pass
@abstractmethod
def hide_lyrics(self):
pass
Ahora, nuestros reproductores solo necesitan adoptar las interfaces que les son útiles, y pueden dejar las que no lo son:
Now, our music players only need to adopt the interfaces that are useful to them, and the can leave out the ones that are not:
class WebPlayer(Playable, LyricsDisplayable):
def play_song(self):
print("Playing song on web player")
def stop_song(self):
print("Stopping song on web player")
def show_lyrics(self):
print("Showing lyrics on web player")
def hide_lyrics(self):
print("Hiding lyrics on web player")
class PhonePlayer(Playable, Downloadable, Deletable):
def play_song(self):
print("Playing song on phone player")
def stop_song(self):
print("Stopping song on phone player")
def download_song(self):
print("Downloading song on phone player")
def delete_song(self):
print("Deleting song on phone player")
class DesktopPlayer(Playable, Downloadable, Deletable):
def play_song(self):
print("Playing song on desktop player")
def stop_song(self):
print("Stopping song on desktop player")
def download_song(self):
print("Downloading song on desktop player")
def delete_song(self):
print("Deleting song on desktop player")
If new platforms emerge with different capabilities, we can always include new interfaces and thus we will not force existing devices to adopt them.
Now our code is much easier to test, understand, and extend.
Benefits of the Interface Segregation Principle
Following the Interface Segregation Principle when designing our interfaces has several benefits:
Reduced Coupling
Since classes only depend on the interfaces they actually use, your code is less entangled and more modular.
Increased Flexibility
Adding new, smaller interfaces to your code is easier and less error prone than modifying larger interfaces on which many parts of the code already depend.
Increased Cohesion
Interfaces contain semantically cohesive functionality, instead of loosely related methods. This results in code that is easier to understand and contribute to.
Easier Testing and Maintenance
Smaller interfaces are easier to test, and errors are easier to isolate and fix without affecting the rest of the codebase.