Better programming: The Liskov Substitution PrincipleJul 29, 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 third of these principles, called the Liskov Substitution Principle. This principle, named after Barbara Liskov, states that in object-oriented programming, instances of a class should be able to be replaced by instances of its subclasses without affecting the functionality of your code.
To learn Python and build the skills to land your first job, check out our Professional Python Developer Bootcamp.
How to apply the Liskov Substitution Principle
Following the example of the music streaming application we saw in the previous article, imagine that there are free and premium accounts. Free accounts have to listen to an ad before a song plays. Premium accounts allow users to skip the ads entirely.
Suppose that in our app there is a class called Player that is in charge of managing media playback. Our class could look like this:
Premium accounts don't play ads, so we could implement our premium player as follows:
We simply disable the play_ad() method. However, this implementation violates the Liskov Substitution Principle and could cause problems in other parts of our app. For example, imagine that we have a part of our app that handles song selection by the user and plays the appropriate song:
By not being careful, we have broken this part of our code and forced our colleagues to rewrite it to accommodate our changes. If we put some thought in our implementation, we can change Player and PremiumPlayer to have the same interface, and hide their specific details inside them. For example, we could rewrite our player to take a parameter ad_interval that decides how often we should display an ad to the user. Then, our PremiumPlayer would simply have a value of infinity for this attribute, therefore ensuring that it will never display an ad.
Furthermore, we can delegate the decision of whether we should display an ad or not to a new function called should_play_ad, that both Player and PremiumPlayer will share.
Then, we can freely replace Player with PremiumPlayer without affecting the code that handles song selection by the user:
Now PremiumPlayer complies with the Liskov Substitution Principle. It will never play an ad because the condition to do so will never be met. However, designing our PremiumPlayer as a special case of the general Player class allows us to safely substitute one for the other without causing problems in other parts of the code.
Benefits of the Liskov Substitution Principle
Following the Liskov Substitution Principle when designing object-oriented class hierarchies has several benefits:
More robust and predictable code
Complying with this principle prevents subtle bugs and unexpected behaviors when replacing a type with a subtype. It helps to prevent regression errors.
Ease of maintenance
It becomes easier to reason about class hierarchies that comply with this principle. It is straightforward to understand how subclasses will behave and avoids surprises.
It allows exploiting polymorphism in our code safely. Other entities in our code can interact with our class hierarchy without having to know the particular details of each class involved.
It incentives subclasses to encapsulate any new behaviors in separate methods instead of modifying the existing ones in the superclass. This preserves the public behavior defined in the superclass.
AI moves fast. We help you keep up with it.
Get a monthly selection of the most groundbreaking advances in the world of AI, top code repositories, and our best articles and tutorials.
We hate SPAM. We will never sell your information, for any reason.