Single Responsibility Principle
You'll learn to
- -Identify when a class has more than one reason to change, and split it cleanly
- -Avoid the common overcorrection of splitting classes so finely the design loses cohesion
The Single Responsibility Principle (SRP) is usually summarized as "a class should do one thing," which is close enough to be useful and vague enough to be misapplied. Robert C. Martin's sharper formulation is the one that actually resolves ambiguity: a class should have only one reason to change.
"One Reason to Change," Not "One Method"
A class can have five methods and still satisfy SRP, as long as all five exist to serve the same responsibility. The violation to watch for is a class that would need to be edited for two unrelated reasons - say, an Employee class that both calculates pay (a business-logic concern owned by Payroll/Finance) and formats a report for printing (a presentation concern owned by whoever maintains reports). Those two concerns change for different reasons, driven by different stakeholders, at different times - which means bundling them into one class means an unrelated formatting tweak risks breaking payroll logic, and vice versa.
# Before: two unrelated responsibilities in one class
class Employee:
def calculate_pay(self) -> float: ...
def save_to_database(self) -> None: ...
def generate_report(self) -> str: ...
# After: each class has exactly one reason to change
class Employee:
def calculate_pay(self) -> float: ...
class EmployeeRepository:
def save(self, employee: Employee) -> None: ...
class EmployeeReportFormatter:
def generate(self, employee: Employee) -> str: ...The Overcorrection: Fragmenting for Its Own Sake
Candidates who have just learned SRP often overcorrect by splitting every class down to a single method, which produces a design that is technically "SRP-compliant" and practically unreadable - fifteen tiny classes for what was one coherent concept. The principle is about separating concerns that change for different reasons, not about minimizing lines-per-class. If two pieces of logic always change together, driven by the same business rule, keeping them in one class is not an SRP violation - it is cohesion.
A useful gut check before splitting a class: "who would ask for this change, and who would ask for that change?" If the answer is the same team/stakeholder for both, you may be over-fragmenting rather than separating real concerns.
You spot an OrderProcessor class that validates an order, charges the payment, and sends a confirmation email, all in one process() method. Is this an SRP violation?
"Yes - it has three methods worth of logic, so it should be three classes."
"Likely yes, but for a specific reason: validation logic changes when business rules change, payment logic changes when we integrate a new payment provider, and email logic changes when marketing wants a new template - three different stakeholders, three different reasons to change. I'd extract OrderValidator, PaymentCharger, and OrderNotifier, and have OrderProcessor orchestrate them rather than implement all three itself."
What is the sharper, more useful formulation of the Single Responsibility Principle?