π 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.
- S: Single Responsibility Principle
- O: Open-Closed Principle
- L: Liskov Substitution Principle
- I: Interface Segregation Principle
- D: Dependency Inversion Principle
β¨ 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.
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()
β 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....")
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)
As an aside: Mypy does catch this: