Published on

SOLID with Python examples

SOLID Principles are five core object-oriented design principles that help you write cleaner, more maintainable, extensible, and testable code. They were popularized by Robert C. Martin (“Uncle Bob”) and are especially relevant in languages like Python, Java, C++, C#, etc.

Below is a clear, practical explanation with Python examples.


🧱 What does SOLID stand for?

LetterPrincipleCore Idea
SSingle ResponsibilityOne class = one reason to change
OOpen / ClosedOpen for extension, closed for modification
LLiskov SubstitutionSubtypes must be usable as their base types
IInterface SegregationDon’t force clients to depend on unused methods
DDependency InversionDepend on abstractions, not concretions

1️⃣ Single Responsibility Principle (SRP)

A class should have only one reason to change.

❌ Bad

class User:
    def save_to_db(self):
        pass

    def send_email(self):
        pass

Why bad?

  • Database logic and email logic are two responsibilities

✅ Good

class User:
    pass

class UserRepository:
    def save(self, user):
        pass

class EmailService:
    def send(self, user):
        pass

✔ Easier to test ✔ Easier to modify ✔ Fewer side effects


2️⃣ Open / Closed Principle (OCP)

Software entities should be open for extension, but closed for modification.

❌ Bad (needs modification every time)

def calculate_salary(employee):
    if employee.type == "full_time":
        return employee.salary
    if employee.type == "contract":
        return employee.hours * employee.rate

✅ Good (use polymorphism)

class Employee:
    def calculate_salary(self):
        raise NotImplementedError

class FullTime(Employee):
    def calculate_salary(self):
        return self.salary

class Contract(Employee):
    def calculate_salary(self):
        return self.hours * self.rate

✔ Add new employee types without touching existing code


3️⃣ Liskov Substitution Principle (LSP)

Objects of a superclass should be replaceable with objects of its subclasses without breaking the program.

❌ Classic violation

class Bird:
    def fly(self):
        pass

class Penguin(Bird):
    def fly(self):
        raise Exception("I can't fly")

Penguin is a Bird, but cannot behave like one.

✅ Better design

class Bird:
    pass

class FlyingBird(Bird):
    def fly(self):
        pass

class Penguin(Bird):
    pass

✔ Subclasses never break expectations


4️⃣ Interface Segregation Principle (ISP)

Clients should not be forced to depend on methods they do not use.

❌ Fat interface

class Machine:
    def print(self): pass
    def scan(self): pass
    def fax(self): pass

A printer that can’t scan still must implement scan().

✅ Split interfaces

class Printer:
    def print(self): pass

class Scanner:
    def scan(self): pass

✔ Smaller, cleaner interfaces ✔ Fewer dummy methods


5️⃣ Dependency Inversion Principle (DIP)

High-level modules should not depend on low-level modules. Both should depend on abstractions.

❌ Tight coupling

class MySQLDatabase:
    def connect(self): pass

class App:
    def __init__(self):
        self.db = MySQLDatabase()

✅ Depend on abstraction

class Database:
    def connect(self):
        raise NotImplementedError

class MySQLDatabase(Database):
    def connect(self):
        pass

class App:
    def __init__(self, db: Database):
        self.db = db

✔ Easier to test (mock databases) ✔ Easy to swap implementations


⚠️ Important note

SOLID is not about over-engineering.

  • Small scripts ❌ don’t need full SOLID
  • Libraries, services, tools ✅ benefit a lot

Think of SOLID as design gravity, not rigid law.