DEV Community

Shubham Kumar Gupta
Shubham Kumar Gupta

Posted on

SOLID Principles Of Programming

SOLID Principles Of Programming

  • SOLID is a set of five design principles that help us to write maintainable and flexible code.
  • They are, SRP (Single Responsibility Principle)
  • OCP (Open-Closed Principle)
  • LSP (Liskov Substitution Principle)
  • ISP (Interface Segregation Principle)
  • DIP (Dependency Inversion Principle)

1. SRP (Single Responsibility Principle)

  • A class should have only a single responsibility.
  • Due to this, it should also have only a single reason to change.

An example of a class that violates SRP:

class User:

    def __init__(self, name, email, password):
        self.name = name
        self.email = email
        self.password = password

    def login(self):
        pass
Enter fullscreen mode Exit fullscreen mode
  • In the above example, the class User is responsible for both managing user data and implementing login.
  • We can make it adhere to SRP by making separate classes for user data and login.
class UserData:

    def __init__(self, name, email, password):
        self.name = name
        self.email = email
        self.password = password

class UserManager:

    def login(self, user_data):
        pass
Enter fullscreen mode Exit fullscreen mode

2. OCP (Open-Closed Principle)

  • A class should be open for extension and closed for modification.
  • That is, we should be able to add new functionality without modifying existing code.

An example of a class that violates OCP:

class Shape:

    def __init__(self, shape_type, dimensions):
        self.shape_type = shape_type
        self.dimensions = dimensions

    def area(self):
        if self.shape_type == 'rectangle':
            pass
        elif self.shape_type == 'circle':
            pass
Enter fullscreen mode Exit fullscreen mode
  • In the above example, we will have to modify the existing code if we need to add a new shape.
  • We can make it adhere to OCP by making abstract classes and methods.
from abc import ABC, abstractmethod

class Shape(ABC):

    def __init__(self, shape_type, dimensions):
        self.dimensions = dimensions

    @abstractmethod
    def area(self):
        pass

class Rectangle(Shape):

    def area(self):
        pass

class Circle(Shape):

    def area(self):
        pass

class Triangle(Shape):

    def area(self):
        pass
Enter fullscreen mode Exit fullscreen mode

3. LSP (Liskov Substitution Principle)

  • A child class should be able to substitute its parent class.
  • That is, any code that works on the parent class should also work on the child class.

An example of a class that violates LSP:

class Animal:

    def __init__(self, name):
        self.name = name

    def eat(self):
        pass

class Bird(Animal):

    def fly(self):
        pass

class Ostrich(Bird):

    def fly(self):
        raise NotImplementedError
Enter fullscreen mode Exit fullscreen mode
  • In the above example, Ostrich is a child class of Bird class but it raises an error when the fly method is called.
  • We can fix this method by making Ostrich a child class of Animal instead of Bird.
class Animal:

    def __init__(self, name):
        self.name = name

    def eat(self):
        pass

class Bird(Animal):

    def fly(self):
        pass

class Ostrich(Animal):
    pass
Enter fullscreen mode Exit fullscreen mode

4. ISP (Interface Segregation Principle)

  • A client should not be forced to depend on methods it does not use.
  • That is, we should keep our interfaces small and focus only on functionality that the client needs.

An example of a class that violates ISP:

class Document:

    def __init__(self, text):
        self.text = text

    def print(self):
        pass

    def fax(self):
        pass

    def scan(self):
        pass
Enter fullscreen mode Exit fullscreen mode
  • In the above example, if a client only needs to print the document, they will still be forced to use the fax and scan method.
  • We can make it adhere to ISP by splitting the Document class into smaller, more focused classes.
class Printable:

    def print(self):
        pass

class Faxable:

    def fax(self):
        pass

class Scanable:

    def scan(self):
        pass

class Document:

    def __init__(self, text):
        self.text = text

class Printer(Document, Printable):
    pass

class Fax(Document, Faxable):
    pass

class Scanner(Document, Scanable):
    pass
Enter fullscreen mode Exit fullscreen mode

5. DIP (Dependency Inversion Principle)

  • Higher-level modules should not depend upon lower-level modules.
  • Instead both should depend on abstractions.

An example of a class that violates DIP:

class Database:

    def insert(self, data):
        pass

class User:

    def __init__(self, name, database):
        self.name = name
        self.database = database

    def save(self):
        pass
Enter fullscreen mode Exit fullscreen mode
  • In the above example, the class user is dependent on the class Database.
  • To make it adhere to DIP, we can create an abstraction for the Database.
from abc import ABC, abstractmethod

class DatabaseInterface(ABC):

    @abstractmethod
    def insert(self, data):
        pass

class Database(DatabaseInterface):

    def insert(self, data):
        pass

class User:

    def __init__(self, name, database):
        self.name = name
        self.database = database

    def save(self):
        pass
Enter fullscreen mode Exit fullscreen mode

REFERENCES

Top comments (0)