DEV Community

Atchukola Naresh
Atchukola Naresh

Posted on

S.O.L.I.D Principles

SOLID Principles

These are the oldest principles but we are still using these priciples due to maintains's
OO systems improve reusabulity and easy testing.

S.O.L.I.D

  • SRP - Single responsibility principle
  • DIP - Dependency inversion principle
  • OCP - Open/closed principle
  • LSP - Liskov substitution principle
  • ISP - Interface segregation principle

SRP - Single responsibility principle:

A class should have only one responsibilty all the methods should be associated with the
that responsibility.

example:

class Email:
    def __init__(self, subject, body, sender, recipient):
         self.subject = subject
        self.body = body
        self.sender = sender
        self.recipient = recipien
class EmailSender:
    def send_email(self, email):
        # Code to send the email using the SMTP server
        pass
class EmailSaver:
    def save_email(self, email):
        # Code to save the email to a database
        pass
Enter fullscreen mode Exit fullscreen mode

In this example, we have separate classes for each responsibility: Email for storing email data, EmailSender for sending emails, and EmailSaver for saving emails to a database. Each class has a single responsibility, making the system easier to understand, modify, and maintain.

OCP - Open/closed principle :

The Open-Closed Principle (OCP) is a fundamental principle in object-oriented programming that states that software entities (classes, modules, functions, etc.) should be open for extension but closed for modification. In other words, the behavior of a system should be extendable without modifying its existing code.

from abc import ABC, abstractmethod

# Abstract class representing a shape
class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

# Concrete implementation of a Rectangle shape
class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

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

# Concrete implementation of a Circle shape
class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

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

# Function that calculates the sum of areas for multiple shapes
def calculate_total_area(shapes):
    total_area = 0
    for shape in shapes:
        total_area += shape.area()
    return total_area
Enter fullscreen mode Exit fullscreen mode

, if we want to add a Triangle shape, we can create a new class Triangle that inherits from Shape and provides its own implementation of the area() method. We can then pass an instance of Triangle to the calculate_total_area() function, and it will work seamlessly without modifying the existing code.

LSP - Liskov substitution principle:

The Liskov Substitution Principle (LSP) is a principle in object-oriented programming that states that objects of a superclass should be replaceable with objects of its subclasses without affecting the correctness of the program. In other words, if a program is designed to work with a certain type of object, it should also work correctly with any subtype of that object.

class Animal:
    def make_sound(self):
        pass

class Dog(Animal):
    def make_sound(self):
        return "Woof!"

class Cat(Animal):
    def make_sound(self):
        return "Meow!"

def animal_sounds(animals):
    for animal in animals:
        print(animal.make_sound())
Enter fullscreen mode Exit fullscreen mode

The animal_sounds() function demonstrates the Liskov Substitution Principle. It takes a list of Animal objects and calls the make_sound() method on each object. By designing the program to work with the Animal superclass, it can also work correctly with any subtype of Animal, such as Dog or Cat.

animals = [Dog(), Cat()]

animal_sounds(animals)
Enter fullscreen mode Exit fullscreen mode

The animal_sounds() function doesn't need to know the exact type of each object in the list. It treats them all as Animal objects and calls the make_sound() method, which is overridden in the subclasses. As a result, it will print "Woof!" and "Meow!" without any issues.

ISP - Interface segregation principle:

The Interface Segregation Principle (ISP) is a principle in object-oriented programming that states that clients should not be forced to depend on interfaces they do not use. It suggests that classes should have narrowly focused interfaces, tailored to the specific needs of the clients that use them, rather than having large, bloated interfaces that encompass more functionality than necessary.

from abc import ABC, abstractmethod

# Interface for a document
class Document(ABC):
    @abstractmethod
    def open(self):
        pass

    @abstractmethod
    def close(self):
        pass

# Interface for a printable document
class Printable(ABC):
    @abstractmethod
    def print_document(self):
        pass

# Interface for a scannable document
class Scannable(ABC):
    @abstractmethod
    def scan_document(self):
        pass

# Implementation of a Word document
class WordDocument(Document, Printable):
    def open(self):
        print("Opening Word document")

    def close(self):
        print("Closing Word document")

    def print_document(self):
        print("Printing Word document")

# Implementation of a PDF document
class PdfDocument(Document, Printable, Scannable):
    def open(self):
        print("Opening PDF document")

    def close(self):
        print("Closing PDF document")

    def print_document(self):
        print("Printing PDF document")

    def scan_document(self):
        print("Scanning PDF document")

# Client code that only depends on the required interfaces
def print_document(printable):
    printable.print_document()

def scan_document(scannable):
    scannable.scan_document()
Enter fullscreen mode Exit fullscreen mode

The client code demonstrates the usage of the specific interfaces. The print_document() function only depends on the Printable interface, allowing it to print any document that implements that interface. Similarly, the scan_document() function only depends on the Scannable interface, allowing it to scan any document that implements that interface.

DIP - Dependency inversion principle :

The Dependency Inversion Principle (DIP) is a principle in object-oriented programming that states that high-level modules should not depend on low-level modules. Instead, both should depend on abstractions. It suggests that the dependencies between modules should be based on abstractions rather than concrete implementations.

from abc import ABC, abstractmethod

# Abstract class representing a notifier
class Notifier(ABC):
    @abstractmethod
    def send_notification(self, message):
        pass

# High-level module that depends on the Notifier abstraction
class NotificationService:
    def __init__(self, notifier):
        self.notifier = notifier

    def send_notification(self, message):
        self.notifier.send_notification(message)

# Low-level module that implements the Notifier abstraction
class EmailNotifier(Notifier):
    def send_notification(self, message):
        print(f"Sending email notification: {message}")

# Low-level module that implements the Notifier abstraction
class SMSNotifier(Notifier):
    def send_notification(self, message):
        print(f"Sending SMS notification: {message}")
Enter fullscreen mode Exit fullscreen mode

By having the NotificationService depend on the Notifier abstraction rather than concrete implementations, we adhere to the Dependency Inversion Principle. The NotificationService is not tightly coupled to specific notifiers (e.g., EmailNotifier or SMSNotifier), but instead depends on the abstraction provided by the Notifier class. This allows for flexibility and extensibility in the system. We can introduce new notifiers by creating classes that implement the Notifier interface, and the NotificationService will work with them without any modifications.

email_notifier = EmailNotifier()
sms_notifier = SMSNotifier()

notification_service = NotificationService(email_notifier)
notification_service.send_notification("Hello, this is an email notification")

notification_service = NotificationService(sms_notifier)
notification_service.send_notification("Hello, this is an SMS notification")
Enter fullscreen mode Exit fullscreen mode

Top comments (0)