DEV Community

Cover image for SOLID Design Principles: Learn the Liskov-Substitution Principle
Alireza Shabani
Alireza Shabani

Posted on

SOLID Design Principles: Learn the Liskov-Substitution Principle

πŸ‘‹ Hey there, fellow software enthusiasts!

I'm Revisto, a passionate software engineer πŸ‘¨β€πŸ’», and I want to dive into the exciting world of SOLID principles.

SOLID is a set of rules and principles that help us create maintainable, reusable, and flexible software designs. These principles guide us in building software that can adapt and grow as our projects evolve. Today, we will focus on the third principle of SOLID.

✨ The Liskov Substitution Principle is named after the brilliant Barbara Liskov. Simply put, it states that a child class should be able to replace its parent class seamlessly. This means that objects of the child class should be able to substitute objects of the parent class without causing any issues or breaking the application's integrity.

Image description

In other words, any piece of code that interacts with objects of a specific type should continue to function correctly even when those objects are replaced with instances of a subtype.

Subtypes must be substitutable for their base types.

(A subtype can be either a class extending another class or a class implementing an interface.)

Let's consider an example to better grasp the concept. Imagine we have a Teleportation class in our codebase. According to the Liskov Substitution Principle, we should be able to substitute the Teleportation class with any of its subclasses, such as Car, Bike or Cycle, without causing any issues or breaking the existing code🀩.

πŸ“” Example

from abc import ABC, abstractmethod

class TransporationDevice(ABC):
        @abstractmethod
        def start_engine(self):
            pass

class Car(TransporationDevice):
        def start_engine(self):
           print("Starting Engine...")

class Bike(TransporationDevice):
        def start_engine(self):
           print("Starting Engine...")

class Cycle(TransporationDevice):
        def start_engine(self):
          # Bicyles don't have engines
          raise NotImplementedError()
Enter fullscreen mode Exit fullscreen mode

❌ The TransporationDevice class doesn't adhere to the Liskov Substitution Principle. When we think about scaling, there is a problem because not all the devices have engines and then there would be a problem.
When that happens, the TransporationDevice class needs to change to support all devices and use other classes for all teleportation kinds. Thus, the current design violates OCP.

from abc import ABC, abstractmethod

class TransporationDevice(ABC):
    @abstractmethod
    def start(self):
        """Steps to start the vehicle"""

class TransporationDeviceWithoutEngine(ABC):
    pass

class TransporationDeviceWithEngine(ABC):        
    @abstractmethod
    def start_engine(self):
        """Process to start the engine"""

class Car(TransporationDeviceWithEngine):

        def start(self):
            # First start engine
            self.start_engine()
            # Do other steps here

        def start_engine(self):
           print("Starting Engine...")

class Bike(TransporationDeviceWithEngine):
        def start(self):
            # First start the engine
            self.start_engine()
            # Do other steps here

        def start_engine(self):
           print("Starting Engine...")


class Cycle(TransporationDeviceWithoutEngine):
        def start(self):
            print("Hit pedals....")
Enter fullscreen mode Exit fullscreen mode

More examples at my GitHub

Benefits of Applying LSP:

By adhering to the Liskov Substitution Principle, we can ensure that our code remains flexible and robust. It reduces the chances of introducing bugs or errors when extending our codebase.

πŸš€ Stay tuned for more articles where we'll explore the other SOLID principles and delve into the exciting world of software engineering.

Thanks for reading. Feel free to comment your thoughts😊. Hope this post was helpful. You can hit me up on Linked In and Github.

Happy coding!

Top comments (1)

Collapse
 
ldrscke profile image
Christian Ledermann

As an aside: Mypy does catch this:

main.py:4: error: Argument 1 of "emit" is incompatible with supertype "Handler"; supertype defines the argument type as "LogRecord"
main.py:4: note: This violates the Liskov substitution principle
main.py:4: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
Enter fullscreen mode Exit fullscreen mode