SOLID Principle

S — Single Responsibility Principle (SRP)

A class should have one job and only one reason to change. Below codes separate printer, saver to different classes

class Report:
    def __init__(self, data):
        self.data = data

class ReportPrinter:
    def print(self, report: Report):
        print(report.data)

class ReportSaver:
    def save(self, report: Report, filename):
        with open(filename, 'w') as f:
            f.write(report.data)

O — Open/Closed Principle (OCP)

Code should be open for extension but closed for modification. For example if you tuck all into one class for different discount like

def get_discount(price, customer_type):
    if customer_type == "student":
        return price * 0.9
    elif customer_type == "senior":
        return price * 0.8

You will have to modify if new customer type is added, instead the better practice with O principle is

class DiscountStrategy:
    def apply(self, price):
        raise NotImplementedError

class StudentDiscount(DiscountStrategy):
    def apply(self, price):
        return price * 0.9

class SeniorDiscount(DiscountStrategy):
    def apply(self, price):
        return price * 0.8

def get_discount(price, strategy: DiscountStrategy):
    return strategy.apply(price)

L — Liskov Substitution Principle (LSP)

Subclasses should be usable as their base class without breaking things.

I — Interface Segregation Principle (ISP)

Don’t force classes to implement methods they don’t use. Python doesn’t have formal interfaces, but we can simulate. Below codes the OldPrinter is forced to implement methods it doesn’t care about, it violated ISP:

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

class OldPrinter(Machine):
    def fax(self): raise NotImplementedError
    def scan(self): raise NotImplementedError

the proper version is

class Printer:
    def print(self): pass

class Scanner:
    def scan(self): pass

class Fax:
    def fax(self): pass

class AllInOnePrinter(Printer, Scanner, Fax):
    def print(self): print("Printing")
    def scan(self): print("Scanning")
    def fax(self): print("Faxing")

D — Dependency Inversion Principle (DIP)

High-level modules should not depend on low-level modules; both depend on abstractions. For example below is bad

class MySQLDatabase:
    def connect(self):
        pass

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

Better one is

class Database:
    def connect(self):
        raise NotImplementedError

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

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

Now App depends on the abstract interface (Database) instead of a concrete class.

“Abstract” = Blueprint
Just like you can have a blueprint for a car without building one yet, you can define a class or method without implementing the details.

It Encourages consistent interfaces. Supports polymorphism (same interface, different behavior). Makes your code more modular and testable.

Abstraction is central in many design patterns. Here are three common ones:

Strategy Pattern

Use: Choose behavior at runtime.

Example: You want different sorting algorithms. It can switch sorting algos without touching class DataProcessor

class SortStrategy(ABC):
    @abstractmethod
    def sort(self, data):
        pass

class QuickSort(SortStrategy):
    def sort(self, data):
        return sorted(data)  # simulate quick sort

class BubbleSort(SortStrategy):
    def sort(self, data):
        # bubble sort logic here
        return data

class DataProcessor:
    def __init__(self, strategy: SortStrategy):
        self.strategy = strategy

    def process(self, data):
        return self.strategy.sort(data)

Template Method Pattern

Use: Define the skeleton of an algorithm in an abstract class, and allow subclasses to fill in the steps.

Factory Pattern

Use: Create objects without specifying the exact class.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.