SOLID Principles in Python: Refactoring for Maintainability and Scale
Overview
Modern software development demands code that is both resilient to change and easy to understand. The
Prerequisites
To get the most out of this tutorial, you should have a solid grasp of DataFrames. You should also understand the basics of Object-Oriented Programming (OOP), specifically classes and methods, though we will also explore functional alternatives.
Key Libraries & Tools
- Python: The primary programming language used for the implementation.
- Pandas: Used for reading CSV files and performing data analysis operations.
- Typing Module: Specifically
Protocol,Any,Callable, andListto enforce structural subtyping. - JSON: The standard format used for the final report output.
Code Walkthrough: The Class-Based SOLID Approach
We begin by breaking down a monolith into specialized components. The first step is defining a Protocol for our metrics. This is a form of structural typing that allows us to define what a "Metric" looks like without forcing inheritance.
from typing import Protocol, Any, Dict
import pandas as pd

class Metric(Protocol): def compute(self, df: pd.DataFrame) -> Dict[str, Any]: ...
Next, we implement specific metrics like `CustomerCountMetric`. Each class has one job: taking a `DataFrame` and returning a specific calculation. This satisfies the **Single Responsibility Principle**.
```python
class CustomerCountMetric:
def compute(self, df: pd.DataFrame) -> Dict[str, Any]:
return {"unique_customers": df["name"].nunique()}
Finally, the SalesExporter class is refactored to use Dependency Inversion. Instead of hardcoding how to read a CSV or calculate a total, it accepts a reader, a writer, and a list of metrics during initialization. The generate method now simply coordinates these objects.
Syntax Notes
Python’s typing.Protocol is a powerful tool for Abstract Base Classes (ABCs), protocols don't require explicit inheritance, making the code cleaner and more flexible.
Practical Examples
Consider a scenario where your business expands from SQLSalesReader that follows the Reader protocol and swap it into your main execution block. The core report logic remains untouched, demonstrating the Open/Closed Principle.
Tips & Gotchas
Avoid the trap of over-engineering. While