SOLID Principles
S – Single Responsibility Principle (SRP)
A class should have only one reason to change — one job or responsibility.
Python Example:
# Violates SRP: User handles persistence + validation + notification
class User:
def save_to_db(self): ... # Persistence
def validate_email(self): ... # Validation
def send_welcome_email(self): ... # Notification
# Follows SRP: Separate responsibilities
class User:
def __init__(self, name, email): ...
class UserRepository:
def save(self, user): ...
class UserValidator:
def is_valid(self, user): ...
class EmailService:
def send_welcome(self, user): ...
O – Open/Closed Principle (OCP)
Classes should be open for extension but closed for modification.
Python Example:
# Violates OCP: Modify class to add new shape
class AreaCalculator:
def area(self, shape):
if shape.type == "circle":
return 3.14 * shape.radius ** 2
elif shape.type == "square": # New type → modify existing code
return shape.side ** 2
# Follows OCP: Extend with new class, no modification
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self): ...
class Circle(Shape):
def area(self): return 3.14 * self.radius ** 2
class Square(Shape):
def area(self): return self.side ** 2
class AreaCalculator:
def area(self, shape: Shape): # Works with any new Shape
return shape.area()
L – Liskov Substitution Principle (LSP)
Subclasses should be substitutable for their base classes without breaking the program.
Python Example:
# Violates LSP: Square forces side equality, breaks Rectangle expectation
class Rectangle:
def set_width(self, w): self.width = w
def set_height(self, h): self.height = h
def area(self): return self.width * self.height
class Square(Rectangle): # Bad inheritance
def set_width(self, w): self.width = self.height = w
def set_height(self, h): self.width = self.height = h
# Follows LSP: Separate hierarchy or composition
class Square:
def __init__(self, side): self.side = side
def area(self): return self.side ** 2
I – Interface Segregation Principle (ISP)
Clients should not be forced to depend on interfaces they don't use — prefer small, specific interfaces.
Python Example:
# Violates ISP: Printer forces unused fax method
class AllInOnePrinter:
def print(self): ...
def scan(self): ...
def fax(self): ... # Scanner-only clients forced to implement
# Follows ISP: Separate interfaces
class Printer:
def print(self): ...
class Scanner:
def scan(self): ...
class MultiFunctionDevice(Printer, Scanner):
def print(self): ...
def scan(self): ...
D – Dependency Inversion Principle (DIP)
High-level modules should not depend on low-level modules; both should depend on abstractions (interfaces).
Python Example:
# Violates DIP: High-level depends on concrete low-level
class MySQLDatabase: # Low-level
def save(self): ...
class UserService: # High-level depends directly
def __init__(self):
self.db = MySQLDatabase() # Tight coupling
# Follows DIP: Depend on abstraction
from abc import ABC, abstractmethod
class Database(ABC):
@abstractmethod
def save(self): ...
class MySQLDatabase(Database):
def save(self): ...
class UserService:
def __init__(self, db: Database): # Inject abstraction
self.db = db
These SOLID principles ensure maintainable, scalable, and testable code by promoting flexibility and reducing coupling. Apply them consistently for robust software design.