CodeOath
← All posts
Architecture & Patterns80 min total · 26 parts

ACID, SOLID, and Common Design Patterns: A Software Design Reference

Contents — Part 7 of 26: Single Responsibility Principle
Part 7 of 26 · ~1 min

Single Responsibility Principle

A class should have exactly one reason to change. Not "one method" or "one line of code" — one reason, meaning one axis of the business or the system along which requirements can independently evolve.

// Violates SRP — three unrelated reasons to change this class:
// a change to how reports are computed, a change to how they're formatted,
// and a change to how email gets sent all land in the same file.
public class SalesReport
{
    public ReportData Compute(DateTime start, DateTime end) { /* query + aggregate sales data */ return null!; }
    public string FormatAsHtml(ReportData data) { /* build an HTML string */ return ""; }
    public void EmailTo(string address, string html) { /* talk to an SMTP server */ }
}
// Each class now has exactly one reason to change.
public class SalesReportCalculator
{
    public ReportData Compute(DateTime start, DateTime end) { /* query + aggregate */ return null!; }
}

public class HtmlReportFormatter
{
    public string Format(ReportData data) { /* build an HTML string */ return ""; }
}

public class EmailSender
{
    public Task SendAsync(string address, string body) => Task.CompletedTask;
}

The practical test for whether a class violates SRP: describe what it does, and see if the description needs the word "and." SalesReport "computes report data and formats it and emails it" is the tell. The payoff of splitting it isn't just tidiness — SalesReportCalculator can now be unit tested with no HTML or SMTP involved at all, and swapping the email provider never touches report-calculation code (and can't accidentally break it).