SOLID principles are a set of guidelines that help developers create software that is maintainable, extensible, and testable. These principles are widely accepted in the software development community and have been used for many years to design high-quality software. In this blog post, we will discuss each of the SOLID principles and provide examples to help you understand how to apply them in your projects.
S - Single-responsibility Principle
O - Open-closed Principle
L - Liskov Substitution Principle
I - Interface Segregation Principle
D - Dependency Inversion Principle
Single Responsibility Principle (SRP)
The Single Responsibility Principle (SRP) states that each class should have a single responsibility or job to perform. A class should only be responsible for doing one thing, and it should not be responsible for doing more than that. The reason for this is that when a class has multiple responsibilities, it becomes harder to maintain, extend, and test.
For Example:
Let's say we have a class called Order that has the responsibility of calculating the total price of an order and also the responsibility of saving the order to a database. This violates the SRP because the class has two responsibilities. Instead, we can create two separate classes, one for calculating the total price of an order and another for saving the order to a database.
class Order:
def __init__(self, items):
self.items = items
def calculate_total(self):
total = 0
for item in self.items:
total += item.price
return total
def save_to_database(self):
# code to save order to database
pass
# Refactored code:
class OrderCalculator:
def __init__(self, items):
self.items = items
def calculate_total(self):
total = 0
for item in self.items:
total += item.price
return total
class OrderSaver:
def save_to_database(self, order):
# code to save order to database
pass
Open-Closed Principle (OCP)
The Open-Closed Principle (OCP) states that software entities (classes, modules, functions, etc.) should be open for extension but closed for modification. In other words, we should be able to extend the behavior of a class without modifying the existing code.
For Example:
Let's say we have a class called Car that has a method called drive(). We want to add a feature that allows the car to fly. Instead of modifying the existing Car class, we can create a new FlyingCar class that inherits from the Car class and adds the new fly() method.
class Car:
def __init__(self):
pass
def drive(self):
# code to drive the car
pass
class FlyingCar(Car):
def __init__(self):
super().__init__()
def fly(self):
# code to fly the car
pass
Liskov Substitution Principle (LSP)
The Liskov Substitution Principle (LSP) states that subtypes must be substitutable for their base types. This means that any instance of a parent class should be able to be replaced by an instance of a subclass without affecting the correctness of the program.
For Example:
Let's say we have a class called Animal that has a method called eat(). We create a subclass called Dog that overrides the eat() method to only eat dog food. We should be able to replace an instance of Animal with an instance of Dog without affecting the correctness of the program.
class Animal:
def __init__(self):
pass
def eat(self):
# code to eat food
pass
class Dog(Animal):
def __init__(self):
super().__init__()
def eat(self):
# code to eat dog food
pass
Interface Segregation Principle (ISP)
The Interface Segregation Principle (ISP) states that clients should not be forced to depend on interfaces they do not use. This means that interfaces should be designed in a way that is specific to the needs of each client, rather than having a single large interface that all clients must use.
For Example:
Let's say we have an interface called Employee that has methods for calculating payroll, printing a badge, and scheduling vacation time. We should create separate interfaces for each of these responsibilities, such as PayrollCalculator, BadgePrinter, and VacationScheduler. This way, clients can depend on only the interfaces they need.
Dependency Invclass Employee:
def __init__(self):
pass
def calculate_payroll(self):
# code to calculate payroll
pass
def print_badge(self):
# code to print a badge
pass
def schedule_vacation(self):
# code to schedule vacation time
pass
# Refactored code:
class PayrollCalculator:
def calculate_payroll(self, employee):
# code to calculate payroll
pass
class BadgePrinter:
def print_badge(self, employee):
# code to print a badge
pass
class VacationScheduler:
def schedule_vacation(self, employee):
# code to schedule vacation time
pass
ersion Principle (DIP)
Dependency Inversion Principle (DIP)
The Dependency Inversion Principle (DIP) states that high-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details. This means that we should depend on interfaces and not on concrete implementations.
For Example:
Let's say we have a class called Report that depends on a class called Database. We should create an interface called DatabaseInterface that both Report and Database implement, and have Report depend on the interface instead of the concrete class. This way, we can easily switch out the Database class for a different implementation without affecting the Report class.
class Database:
def __init__(self):
pass
def read(self):
# code to read data from database
pass
class Report:
def __init__(self, database):
self.database = database
def generate_report(self):
data = self.database.read()
# code to generate report from data
pass
# Refactored code:
class DatabaseInterface:
def read(self):
pass
class Database(DatabaseInterface):
def read(self):
# code to read data from database
pass
class Report:
def __init__(self, database_interface):
self.database_interface = database_interface
def generate_report(self):
data = self.database_interface.read()
# code to generate report from data
pass
0 Comments