Catalog / SOLID Principles / Single Responsibility Principle (SRP)
SOLID

Single Responsibility Principle (SRP)

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

Intent & Objective

Gather together the things that change for the same reasons. Separate things that change for different reasons. Keep each class, module, or function focused on a single well-defined responsibility.

๐Ÿ‘ฅ Real-World Human Analogies & Social Roles
2 Intuitive Scenarios
Chefโ€™s Specialized Knives vs Swiss Army Knife Professional Kitchen

"A chef keeps specialized single-purpose knives: a chef knife for slicing, a paring knife for peeling, and a serrated knife for bread, instead of a clumsy 20-blade pocket tool."

Hospital Specialized Departments Healthcare Operations

"Cardiology diagnoses heart disease, pharmacy dispenses medicine, and billing handles insurance claims. Each department has one single responsibility."

The Problem

In monolithic "God Classes", unrelated responsibilities are tightly bound together. When business rules for invoice formatting change, you risk breaking payment calculation or database persistence logic. SRP isolates business rules from formatting, persistence, and external communication.

The Solution

Decompose the multi-purpose class into dedicated classes with cohesive responsibilities: one for core domain calculation, one for serialization/formatting, and one for persistence/repository operations.

Key Participants & Architectural Roles

Domain Entity / Calculator
Encapsulates the core business calculations only
Formatter / Presenter
Handles formatting data for presentation or export (JSON, HTML, etc.)
Repository / Persistence
Handles database, file, or network storage

Code Samples (Traditional OOP vs Functional Programming)

Available in C++, Python, and Java

Paradigm Contrast: Traditional OOP vs Functional / Data-Oriented

In OOP, SRP is achieved by separating responsibilities into distinct classes (e.g. Invoice, InvoiceRepository, InvoiceFormatter). In Functional Programming, SRP is natural and fundamental: each pure function performs one exact transformation (Input -> Output) without side effects. Persistence and I/O are kept at the boundaries of the system.

Advantages

  • โœ“ High cohesion and low coupling
  • โœ“ Easier unit testing because each component has a minimal surface area
  • โœ“ Minimizes merge conflicts in team environments
  • โœ“ Simplifies maintenance when specific requirements change

Trade-offs

  • โœ— Increases total number of classes and files in the project
  • โœ— Requires careful boundary definition to prevent over-fragmentation

When to Apply

  • โ†’ When a class performs multiple unrelated tasks (e.g. calculation + formatting + database saving)
  • โ†’ When different user roles (e.g. Accountant vs UI Designer vs DBA) demand changes to the same file
  • โ†’ When unit tests require complex mocking of unrelated dependencies

When to Avoid

  • ! For trivial helper scripts where separating tiny functions creates unnecessary cognitive friction
  • ! When responsibilities are deeply intertwined and separation would create anemic models with excessive getters/setters