Liskov Substitution Principle (LSP)

The Liskov Substitution Principle (LSP) is one of the SOLID principles of object-oriented programming. It states:

“Objects of a superclass should be replaceable with objects of its subclasses without altering the correctness of the program.”

In simpler terms, if a class A is a parent class, and B is a subclass of A, you should be able to use B objects wherever A objects are expected, and the program should still work correctly.

Example: Understanding LSP with Birds

Imagine you have a class Bird with a method fly(). All birds are supposed to fly, so the method is included in the base class:

class Bird:
    def fly(self):
        print("Flying!")

Now, you create subclasses:

  1. Sparrow: Sparrows can fly, so everything works fine:pythonKopiraj kod
class Sparrow(Bird):
    pass  # No changes needed

2.Penguin: Penguins are birds, but they cannot fly. If you implement Penguin like this:

class Penguin(Bird):
    def fly(self):
        raise NotImplementedError("Penguins cannot fly!")

You are violating LSP because if the program expects a Bird and gets a Penguin, it will break when fly() is called.

Why Does This Violate LSP?

The substitution principle says that subclasses must fulfill the expectations set by their parent class. In this case, the Bird class promises that every bird can fly. However, a Penguin does not meet that expectation.

Solution: Follow LSP

To fix this, you can rethink your design. Instead of assuming all birds can fly, separate flying behavior into its own abstraction:

from abc import ABC, abstractmethod

class Bird(ABC):
    @abstractmethod
    def describe(self):
        pass

class FlyingBird(Bird):
    @abstractmethod
    def fly(self):
        pass

class Sparrow(FlyingBird):
    def describe(self):
        return "I am a sparrow."

    def fly(self):
        print("Flying high!")

class Penguin(Bird):
    def describe(self):
        return "I am a penguin. I cannot fly, but I swim well!"

Here’s why this works:

  • Bird now only defines common behavior for all birds, such as describe().
  • FlyingBird adds behavior specific to birds that can fly.
  • A Penguin only implements Bird, while a Sparrow implements both Bird and FlyingBird.

Key Takeaway

The Liskov Substitution Principle ensures that:

  1. Subclasses behave in a way that respects the expectations of their parent class.
  2. Your program doesn’t break when you replace a superclass object with a subclass object.

By adhering to LSP, your code becomes more robust and easier to maintain.

About the author

Vili M, PhD

With an extensive experience in programming, Vili has dedicated his career to developing innovative solutions and advancing technology. As an expert in programming, electromagnetic fields, robotics, and teaching skills, he combines academic knowledge with practical expertise to deliver impactful results.