SOLID Principle Describe with Python Example

SOLID Principle Describe with Python Example

The SOLID principles are a set of guidelines that aim to make software design more understandable, flexible, and maintainable. These principles were first described by Robert C. Martin, also known as Uncle Bob, in his book "Agile Software Development, Principles, Patterns, and Practices."

SOLID is an acronym that stands for the following five principles:

1.Single Responsibility Principle (SRP)

2.Open-Closed Principle (OCP)

3.Liskov Substitution Principle (LSP)

4.Interface Segregation Principle (ISP)

5. Dependency Inversion Principle (DIP)

Below, we'll go through each of these principles and provide a Python code example to illustrate how they can be applied in practice.

Single Responsibility Principle (SRP) The Single Responsibility Principle states that a class should have only one reason to change. In other words, a class should have a single, well-defined responsibility.

This helps to make a system more flexible and maintainable, because changes to a class only need to be made in one place, rather than having to be made in multiple places throughout the system.

Here's an example of how the Single Responsibility Principle can be applied in Python:

class User:
    def __init__(self, name, email):
        self.name = name
        self.email = email

    def get_name(self):
        return self.name

    def get_email(self):
        return self.email

class UserManager:
    def __init__(self):
        self.users = []

    def add_user(self, user):
        self.users.append(user)

    def get_users(self):
        return self.users

In this example, the User class has a single responsibility: to store and retrieve information about a user. The UserManager class, on the other hand, has the responsibility of managing a list of users. This separation of responsibilities makes it easier to understand and maintain the code.

Open-Closed Principle (OCP) The Open-Closed Principle states that a class should be open for extension, but closed for modification. This means that a class should be designed in such a way that it can be extended to add new functionality, without having to modify the existing code.

Here's an example of how the Open-Closed Principle can be applied in Python:

class Shape:
    def area(self):
        pass

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

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

    def area(self):
        return 3.14 * self.radius * self.radius

class AreaCalculator:
    def __init__(self, shapes):
        self.shapes = shapes

    def calculate_area(self):
        total_area = 0
        for shape in self.shapes:
            total_area += shape.area()
        return total_area

In this example, the Shape class is an abstract class that defines the area method. The Rectangle and Circle classes are concrete implementations of the Shape class, and they each have their own implementation of the area method.

The AreaCalculator class is responsible for calculating the total area of a list of shapes. It does this by iterating over the list of shapes and calling the area method on each one.

This design follows the Open-Closed Principle, because if we want to add a new type of shape, we can simply create a new class that extends the Shape class and provides its own implementation of the area method. We don't have to modify the AreaCalculator class at all.

Liskov Substitution Principle (LSP) The Liskov Substitution Principle states that a subclass should be able to be used in place of its superclass, without causing any issues. In other words, a subclass should be a substitutable implementation of its superclass.

Here's an example of how the Liskov Substitution Principle can be applied in Python:

class Vehicle:
    def start(self):
        pass

    def stop(self):
        pass

class Car(Vehicle):
    def start(self):
        print("Starting car")

    def stop(self):
        print("Stopping car")

class Motorcycle(Vehicle):
    def start(self):
        print("Starting motorcycle")

    def stop(self):
        print("Stopping motorcycle")

def test_vehicle(vehicle):
    vehicle.start()
    vehicle.stop()

car = Car()
motorcycle = Motorcycle()

test_vehicle(car)
test_vehicle(motorcycle)

In this example, the Vehicle class is an abstract class that defines the start and stop methods. The Car and Motorcycle classes are concrete implementations of the Vehicle class, and they each have their own implementation of the start and stop methods.

The test_vehicle function takes a Vehicle object as an argument and calls the start and stop methods on it. Because the Car and Motorcycle classes are substitutable implementations of the Vehicle class, we can pass either a Car object or a Motorcycle object to the test_vehicle function, and it will work correctly.

Interface Segregation Principle (ISP) The Interface Segregation Principle states that clients should not be forced to depend on interfaces they don't use. In other words, a client should not have to depend on methods that it doesn't use.

Here's an example of how the Interface Segregation Principle can be applied in Python:

class Animal:
    def eat(self):
        pass

    def sleep(self):
        pass

    def speak(self):
        pass

    def fly(self):
        pass

class Dog(Animal):
    def eat(self):
        print("Eating")

    def sleep(self):
        print("Sleeping")

    def speak(self):
        print("Barking")

class Cat(Animal):
    def eat(self):
        print("Eating")

    def sleep(self):
        print("Sle

In this example, the Animal class defines four methods: eat, sleep, speak, and fly. These methods may not be applicable to all animals. For example, not all animals can fly.

The Dog and Cat classes are concrete implementations of the Animal class, and they each implement the methods that are applicable to them. The Dog class implements the eat, sleep, and speak methods, but not the fly method, because dogs cannot fly. Similarly, the Cat class implements the eat, sleep, and speak methods, but not the fly method because cats cannot fly.

This design follows the Interface Segregation Principle because the Dog and Cat classes are not forced to depend on the fly method, which they do not use.

Dependency Inversion Principle (DIP) The Dependency Inversion Principle states that high-level modules should not depend on low-level modules. Instead, both should depend on abstractions.

Here's an example of how the Dependency Inversion Principle can be applied in Python:

class Database:
    def save(self, data):
        pass

    def load(self):
        pass

class User:
    def __init__(self, database):
        self.database = database

    def save(self):
        data = {"name": self.name, "email": self.email}
        self.database.save(data)

    def load(self):
        data = self.database.load()
        self.name = data["name"]
        self.email = data["email"]

In this example, the Database class is a low-level module that provides methods for saving and loading data. The User class is a high-level module that depends on the Database class to save and load user data.

However, rather than depending directly on the Database class, the User class depends on an abstraction (i.e., the database attribute). This means that the User class can work with any implementation of the Database class, as long as it provides the necessary save and load methods.

This design follows the Dependency Inversion Principle because the high-level User class depends on an abstraction (the database attribute), rather than on a specific implementation of the Database class.

I hope this has been a helpful overview of the SOLID principles and how they can be applied in Python. Applying these principles can help to make your software design more understandable, flexible, and maintainable.

Did you find this article valuable?

Support Giasuddin Blog by becoming a sponsor. Any amount is appreciated!