SOLID Principles

Five essential object-oriented design principles for maintainable and scalable software

SOLID Principles at a Glance

Principle Description Mnemonic
SRPOne reason to changeOne job per class
OCPOpen for extension, closed for modificationPlug in new features
LSPSubtypes replace base typesReplaceable children
ISPSmall, focused interfacesOnly what you use
DIPDepend on abstractionsUse interfaces
Single Responsibility Principle (SRP)

Report

Report
title, content
ReportPrinter
print_report(report)
Each class should have only one reason to change.
(One job per class)
class Report:
    def __init__(self, title, content):
        self.title = title
        self.content = content

class ReportPrinter:
    def print_report(self, report):
        print(f"Title: {report.title}")
        print(f"Content: {report.content}")
Open/Closed Principle (OCP)

Exporters

PDF HTML
ReportExporter
export(report)
Open for extension, closed for modification.
(Add new exporters without changing existing code)
class ReportExporter:
    def export(self, report):
        raise NotImplementedError

class PDFExporter(ReportExporter):
    def export(self, report):
        print(f"Exporting '{report.title}' as PDF...")

class HTMLExporter(ReportExporter):
    def export(self, report):
        print(f"Exporting '{report.title}' as HTML...")
Liskov Substitution Principle (LSP)

Bird Hierarchy

Bird
fly()
Sparrow
fly()
Subtypes must be substitutable for their base types.
(Replaceable children)
class Bird:
    def fly(self):
        print("Bird is flying")

class Sparrow(Bird):
    def fly(self):
        print("Sparrow is flying")

def make_bird_fly(bird):
    bird.fly()

make_bird_fly(Sparrow())  # Output: Sparrow is flying
Interface Segregation Principle (ISP)

Interfaces

Printable
print()
Saveable
save()
Clients should not be forced to depend on methods they do not use.
(Small, focused interfaces)
class Printable:
    def print(self):
        raise NotImplementedError

class Saveable:
    def save(self):
        raise NotImplementedError

class Document(Printable, Saveable):
    def print(self):
        print("Printing document...")

    def save(self):
        print("Saving document...")
Dependency Inversion Principle (DIP)

Database Abstraction

Database
connect()
MySQLDatabase
connect()
High-level modules should not depend on low-level modules. Both should depend on abstractions.
(Depend on interfaces, not concrete classes)
class Database:
    def connect(self):
        raise NotImplementedError

class MySQLDatabase(Database):
    def connect(self):
        print("Connecting to MySQL database...")

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

    def start(self):
        self.db.connect()

# Usage
db = MySQLDatabase()
service = AppService(db)
service.start()