Skip to content

Latest commit

 

History

History
160 lines (112 loc) · 3.09 KB

File metadata and controls

160 lines (112 loc) · 3.09 KB

SOLID Principles

This document outlines the SOLID principles with examples relevant to the Accellens codebase. All backend and frontend code should strive to adhere to these principles to maintain scalability and testability.

Single Responsibility Principle (SRP)

A class or module should have one, and only one, reason to change.

Violation

class ReportGenerator:
    def generate_report(self, data):
        # Format data
        report = f"Title: {data['title']}..."

        # Save to file
        with open('report.txt', 'w') as f:
            f.write(report)

        # Email report
        smtp.send_message(report)

Reason to change: Logic changes for formatting, saving, OR emailing.

Adherence

class ReportFormatter:
    def format(self, data) -> str: ...

class ReportSaver:
    def save(self, report: str, path: str): ...

class EmailService:
    def send(self, content: str): ...

Open/Closed Principle (OCP)

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

Violation

def calculate_area(shape):
    if isinstance(shape, Rectangle):
        return shape.width * shape.height
    elif isinstance(shape, Circle):
        return 3.14 * shape.radius ** 2

Issue: Adding a new shape requires modifying calculate_area.

Adherence

class Shape(ABC):
    @abstractmethod
    def area(self) -> float: ...

class Rectangle(Shape):
    def area(self) -> float:
        return self.width * self.height

class Circle(Shape):
    def area(self) -> float:
        return math.pi * self.radius ** 2

Liskov Substitution Principle (LSP)

Subtypes must be substitutable for their base types.

Violation

class Bird:
    def fly(self): ...

class Ostrich(Bird):
    def fly(self):
        raise NotImplementedError("Ostriches can't fly")

Issue: Ostrich breaks the contract of Bird.

Adherence

class Bird:
    def move(self): ...

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

class Ostrich(Bird):
    def move(self):
        self.run()

Interface Segregation Principle (ISP)

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

Violation

interface Worker {
    work(): void;
    eat(): void;
}

class Robot implements Worker {
    work() { ... }
    eat() { throw new Error("Robots don't eat"); }
}

Adherence

interface Workable {
    work(): void;
}

interface Eatable {
    eat(): void;
}

class Robot implements Workable { ... }
class Human implements Workable, Eatable { ... }

Dependency Inversion Principle (DIP)

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

Violation

class UserService:
    def __init__(self):
        self.db = PostgresDatabase() # Hard dependency

Adherence

class DatabaseInterface(ABC):
    @abstractmethod
    def save(self, data): ...

class UserService:
    def __init__(self, db: DatabaseInterface):
        self.db = db # Dependency injection

class PostgresDatabase(DatabaseInterface): ...