What are SOLID Principles in Python for Better Software Design

Share Your Love

SOLID Principles in Python are a set of principles that were developed to help software developers design more maintainable and flexible software systems. The acronym SOLID stands for:

  • S – Single Responsibility Principle (SRP)
  • O – Open/Closed Principle (OCP)
  • L – Liskov Substitution Principle (LSP)
  • I – Interface Segregation Principle (ISP)
  • D – Dependency Inversion Principle (DIP)

Below is a proper explanation of each principle with an example:

Single Responsibility Principle (SRP): This principle states that each class or module should have only one responsibility and that responsibility should be entirely encapsulated by that class or module. In other words, a class should have only one reason to change. This makes the code easier to understand, test, and maintain.

Example Of Single Responsibility Principle (SRP): Suppose you have a class that manages both user authentication and user data storage. This class violates the SRP because it has two responsibilities. To apply the SRP, you could split this class into two separate classes: one for user authentication and another for user data storage.

Open/Closed Principle (OCP): This principle states that classes or modules should be open for extension, but closed for modification. This means that you should be able to add new functionality to a class or module without modifying its existing code. This makes the code more flexible and less prone to bugs.

Example Of Open/Closed Principle (OCP): Suppose you have a class that calculates the area of a shape. This class has a method that calculates the area of a rectangle. To apply the OCP, you could create a new subclass of the shape class that overrides the method to calculate the area of a circle. This way, you can add new shapes without modifying the existing code.

Liskov Substitution Principle (LSP): This principle states that a subclass should be able to be substituted for its superclass without affecting the correctness of the program. In other words, the behaviour of the subclass should be consistent with the behaviour of the superclass. This makes the code more reusable and easier to maintain.

Example Of Liskov Substitution Principle (LSP): Suppose you have a class that represents a rectangle. This class has methods for setting the width and height of the rectangle. To apply the LSP, you could create a new subclass of the rectangle class that represents a square. This subclass would have to override the methods for setting the width and height to ensure that they always set both values to the same value.

Interface Segregation Principle (ISP): This principle states that clients should not be forced to depend on interfaces that they do not use. In other words, you should split large interfaces into smaller and more specific ones so that clients can depend only on the interfaces they need. This makes the code more flexible and easier to maintain.

Example Of Interface Segregation Principle (ISP): Suppose you have an interface that represents a printer. This interface has methods for printing, scanning, and copying. To apply the ISP, you could split this interface into three smaller interfaces: one for printing, one for scanning, and one for copying. This way, clients can depend only on the interfaces they need.

Dependency Inversion Principle (DIP): This principle states that high-level modules should not depend on low-level modules. Instead, both should depend on abstractions. This means that you should not depend on concrete implementations, but on abstractions that can be easily replaced. This makes the code more flexible and easier to maintain.

Example Of Dependency Inversion Principle (DIP): Suppose you have a class that depends on a database connection. This class creates the connection object directly, violating the DIP. To apply the DIP, you could use a dependency injection framework to inject the connection object into the class at runtime. This way, the class depends on an abstraction of the database connection, not on a concrete implementation.

By using these principles, developers can create software that is more modular, flexible, and easier to maintain. These principles also help to reduce the risk of introducing bugs and to make the code more testable.

SOLID principles with Python OOPs examples:

Now, the question is how the SOLID principles can be applied in an object-oriented programming (OOP) context, below is the examples of each principle.

  1. Single Responsibility Principle (SRP):

Suppose you have a class called Order that handles both the order processing and the communication with the customer. This class violates the SRP because it has two responsibilities. To apply the SRP, you could split this class into two separate classes: Order and OrderNotification. The Order the class would be responsible for order processing, while the OrderNotification class would be responsible for communicating with the customer.

class Order:
    def __init__(self, order_id, customer, items):
        self.order_id = order_id
        self.customer = customer
        self.items = items

    def process(self):
        # process the order

class OrderNotification:
    def __init__(self, order_id, customer):
        self.order_id = order_id
        self.customer = customer

    def notify(self):
        # send notification to the customer
  1. Open/Closed Principle (OCP):

Suppose you have a class called Shape that has a method to calculate the area of a shape. To apply the OCP, you could create a new subclass Shape called Circle that overrides the method to calculate the area of a circle.

class Shape:
    def area(self):
        pass

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14 * self.radius ** 2
  1. Liskov Substitution Principle (LSP):

Suppose you have a class called Vehicle that has a method to move. To apply the LSP, you could create a new subclass of Vehicle called Car that overrides the method to move.

class Vehicle:
    def move(self):
        pass

class Car(Vehicle):
    def move(self):
        # move the car
  1. Interface Segregation Principle (ISP):

Suppose you have an interface called Animal that has methods for eating, sleeping, and walking. To apply the ISP, you could split this interface into three smaller interfaces: Eatable, Sleepable, and Walkable. Each animal class would then implement only the interfaces it needs.

class Eatable:
    def eat(self):
        pass

class Sleepable:
    def sleep(self):
        pass

class Walkable:
    def walk(self):
        pass

class Cat(Eatable, Sleepable, Walkable):
    def eat(self):
        # eat food

    def sleep(self):
        # sleep

    def walk(self):
        # walk
  1. Dependency Inversion Principle (DIP):

Suppose you have a class called UserService that depends on a database connection. This class creates the connection object directly, violating the DIP. To apply the DIP, you could use a dependency injection framework to inject the connection object into the UserService class at runtime.

class UserService:
    def __init__(self, db_connection):
        self.db_connection = db_connection

    def get_user(self, user_id):
        # query the database using the db_connection object

In this example, the UserService class depends on an abstraction of the database connection (db_connection), not on a concrete implementation.

That’s it now hope you understand and having any questions please comment below.

Share Your Love
Avatar photo
Lingaraj Senapati

Hey There! I am Lingaraj Senapati, the Founder of lingarajtechhub.com My skills are Freelance, Web Developer & Designer, Corporate Trainer, Digital Marketer & Youtuber.

Articles: 411

Newsletter Updates

Enter your email address below to subscribe to our newsletter