ReviseAlgo Logo

SOLID Principles

Single Responsibility Principle

A class should have one reason to change

Last Updated: June 26, 2026 20 min read

The Single Responsibility Principle (SRP) is the first of the SOLID design principles, introduced by Robert C. Martin ("Uncle Bob"). It states that "a class should have one, and only one, reason to change." SRP is the foundation of high cohesion and low coupling in Object-Oriented Design.

Single Responsibility Principle

✘ BAD God Class
UserManager
createUser() / deleteUser()
sendEmail() / sendSMS()
saveToDatabase() / query()
generatePDF() / exportCSV()
4 reasons to change → fragile code
GOOD Focused Classes
UserService
createUser() • deleteUser()
NotificationService
sendEmail() • sendSMS()
UserRepository
save() • query()
ReportGenerator
generatePDF() • exportCSV()
Each class has 1 reason to change → stable code

1. Learning Objectives

  • Understand the formal definition of the Single Responsibility Principle.
  • Identify signs of a "God Class" or low cohesion.
  • Differentiate between a class's "responsibility" and its "actions."
  • Analyze "Before vs. After" class UML diagrams to trace SRP refactoring paths.
  • Implement cleanly separated class components in Java, Python, and C++.

2. Problem Statement

When starting a new project, it is easy to dump multiple features into a single class. For example, a single Employee class might handle employee metadata, database persistence operations, salary calculations, and PDF report generation.

This "God Class" design leads to several problems:

  • Fragile Code: If the database schema changes, we must edit the Employee class. If the salary calculation rules change, we edit the same class. If the PDF template layout changes, we edit it again. Multiple reasons to change make the class fragile and prone to regression bugs.
  • Merge Conflicts: Different developers working on separate tasks (e.g. database migration vs report layout) will commit modifications to the same file, resulting in frequent merge conflicts.
  • Testing Difficulties: Unit testing the salary calculation logic requires setting up database connections or mocking file writer streams.

3. Real-world Analogy

Think of a Professional Kitchen Staff:

  • The God Employee (Violates SRP): A single person who takes customer orders, cooks the meals, washes the dishes, maintains the inventory, and handles the cash register. If they get sick, the entire restaurant shuts down. If they make a mistake in inventory, it ruins the cooking schedules.
  • The Specialized Staff (SRP Compliant):
    • Waiter: Communicates with customers and takes orders.
    • Chef: Focuses on food preparation.
    • Cashier: Manages financial transactions.

If the menu changes, only the Chef's workflow is impacted. If the payment terminal changes, only the Cashier is affected. Each person has a single reason to change.

4. Theory

Robert C. Martin defines a responsibility as a "reason to change." He clarifies that a reason to change is linked to the actors (users, stakeholder groups) who request the change.

  • The Finance Department (CFO) is the actor responsible for requesting changes to salary calculations.
  • The HR Department (COO) is the actor responsible for requesting changes to employee reporting and metadata structure.
  • The DBA/DevOps Team is the actor responsible for requesting changes to data persistence mechanisms.

If a single class is responsible for answering to all three actors, it violates SRP. The class must be split so that each class serves a single actor.

5. Visual Diagrams (Before vs. After SRP structures)

Before: The God Class

Employee
- id: String
- name: String
- baseSalary: double
+ calculateSalary(): double (Actor: CFO)
+ saveToDatabase(): void (Actor: DBA)
+ generateReportPDF(): String (Actor: HR)

After: SRP Compliant Design

Employee (Entity)
- id: String
- name: String
- baseSalary: double
SalaryCalculator
+ calculateSalary(Employee): double
EmployeeRepository
+ save(Employee): void
+ findById(id): Employee
EmployeeReportGenerator
+ generateReportPDF(Employee): String

6. Syntax Explanation

SRP is implemented by decomposing single classes into multiple smaller, focused classes:

  • Java: Separate domain models (plain POJOs containing data) from business logic services and data access objects (DAOs/Repositories).
  • Python: Extract logic to separate module files or dedicated class structures, avoiding placing database interactions inside raw data models.
  • C++: Keep header definitions decoupled. Maintain data structures in separate structs or classes, and place calculation routines in utility namespaces or dedicated classes.

7. Step-by-Step Implementation

  • Step 1: Identify classes containing multiple methods that serve different actors (e.g. database access mixed with UI logic).
  • Step 2: Extract the data components into a clean entity class containing only fields, getters, and setters (e.g. Employee).
  • Step 3: Extract business calculations into a service class (e.g. SalaryCalculator).
  • Step 4: Extract database operations into a repository class (e.g. EmployeeRepository).

8. Complete Code (Mini Project)

9. Code Walkthrough

In the refactored code:

  • The Employee class is a simple data holder. It has no methods that implement business rules.
  • The database persistence logic has been moved to EmployeeRepository. If we change the database from MySQL to MongoDB, we only modify this repository class.
  • The salary calculations are encapsulated within SalaryCalculator. Changes to bonuses or tax formulas are isolated here.
  • The report layout logic is isolated in EmployeeReportGenerator.

10. Execution Flow

  1. Instantiations: The client instantiates the Employee data entity, EmployeeRepository, and EmployeeReportGenerator.
  2. Persistence: Call repository.save(emp) to write data to database.
  3. Report Generation: Call reportGenerator.generateReport(emp). This helper calls SalaryCalculator internally to fetch calculation parameters before constructing the PDF output.

11. Internal Working

At the JVM level, separating concerns into smaller classes improves classloader caching and reduces memory utilization:

  • In a monolithic "God Class" architecture, loading a single instance of GodEmployee allocates memory for all attributes and loads bytecode for database connectors, UI formatters, and calculation strategies. This creates a larger heap footprint.
  • By splitting these into stateless services (e.g. SalaryCalculator), we can register the services as singleton instances. The client thread only passes the lightweight Employee data entity to the service methods. The thread stack frame handles the reference passing, and the heap contains only a single instance of the service, reducing GC overhead.

12. Complexity Analysis

  • Time Complexity: $O(1)$ delegation overhead. Extracted classes execute methods directly.
  • Space Complexity: $O(1)$ heap overhead. Moving logic to stateless singleton service instances prevents memory bloat.

13. Best Practices

  • One Reason to Change: Ensure each class answers to a single business stakeholder group (actor).
  • High Cohesion: Keep related methods grouped together. If a class contains two methods that do not share any class-level variables, they should likely be split into separate classes.
  • Avoid generic names: Avoid naming classes "Manager", "Processor", or "Handler". Choose specific names like EmployeeRepository or SalaryCalculator to enforce a single responsibility.

14. Common Mistakes

  • Over-splitting classes: Breaking classes down so far that every method resides in its own class. This creates a highly complex system with too many files and excessive indirection, violating the KISS principle.
  • Confusing SRP with single action: Believing a class can have only one method. A class can have multiple methods as long as they all support the same responsibility (e.g. EmployeeRepository can have save(), delete(), update(), and findById() because they all serve data persistence).
  • Mixing Layers: Mixing database logic with UI display logic or business rules. Keep these concerns separated into distinct application layers.

15. Interview Questions

Q: How do you identify a violation of the Single Responsibility Principle?
Answer: Look for classes with names like UserManager or AppController that import packages from multiple layers (e.g., UI formatting, SQL database connection, and calculations). Another indicator is having to update the same class file for unrelated requirement changes (e.g. database schema change vs layout modification).
Q: Who is the "actor" behind a class's responsibility?
Answer: An actor is a business stakeholder group (e.g. HR, Finance, Admin) that has the authority to request changes to a feature. SRP states that a class should answer to only one actor, separating code so that CFO changes do not impact HR logic.
Q: What is the risk of having low cohesion in a class?
Answer: Low cohesion means the methods inside a class do not have much in common. This makes the class harder to understand, test, and reuse, and increases the risk of side effects where modifying one method breaks an unrelated part of the class.

16. Practice Exercises

  • Easy: Identify and refactor a Product class that holds product attributes and prints barcode images to the printer stream.
  • Medium: Refactor an OrderManager class containing calculateTotals(), saveOrder(), sendConfirmationEmail(), and printReceipt(). Split it into four focused classes.
  • Hard: Design a thread-safe log processing engine. Ensure the logging framework separates log level filtering logic, file writing logic, and console output formatting logic into three distinct components to maintain high cohesion.

17. Challenge Problem

Design an e-commerce checkout module. The system must process payment details, deduct stock inventory, calculate shipping costs based on distance, and email receipt confirmations. Write a series of classes that implement this checkout flow while strictly adhering to the Single Responsibility Principle, and demonstrate how you verify the design using isolated unit tests for each component.

18. Summary

  • SRP states that a class should have only one reason to change, serving a single actor.
  • Violating SRP creates "God Classes" that are fragile, hard to test, and prone to merge conflicts.
  • SRP encourages high cohesion and low coupling by separating concerns into distinct classes.
  • Stateless service classes and DAOs/Repositories help separate business logic from data model structures.

19. Cheat Sheet

Principle Core Rule Indicator of Violation How to Refactor
SRP One class = One reason to change Class imports layers from multiple domains (SQL, UI, Biz) Split into Entity, Service, and Repository components

20. Quiz

1. Who introduced the SOLID design principles, including SRP?

A) Martin Fowler
B) Robert C. Martin (Correct)
C) Linus Torvalds

2. What is the formal definition of the Single Responsibility Principle?

A) A class should have only one method
B) A class should have one, and only one, reason to change (Correct)
C) High-level modules should not depend on low-level modules

3. How is a "reason to change" defined in SRP?

A) By the number of lines of code in a class
B) By the business actor or stakeholder group requesting changes to a feature (Correct)
C) By compiler performance bottlenecks

4. What is a common indicator of a class violating SRP?

A) Having multiple static constants
B) A class importing and mixing layers from database persistence, calculations, and UI reporting (Correct)
C) Using composition

5. Why are "God Classes" dangerous in a large project?

A) They execute too quickly at runtime
B) They are fragile, hard to test, and cause frequent merge conflicts (Correct)
C) They prevent JIT optimizations

6. What does high cohesion in a class mean?

A) The methods inside the class are closely related and share class-level state (Correct)
B) The class depends on multiple databases
C) The class inherits from multiple base classes

7. What is a risk of over-applying the SRP principle?

A) Creating too many small, fragmented classes, increasing indirection and complexity (Correct)
B) Memory footprint reduction
C) Slower JIT compilation

8. Can a repository class have multiple methods under SRP?

A) No, it can only have one method
B) Yes, as long as all methods (e.g. save, find, delete) serve the same persistence responsibility (Correct)
C) Only if they are static

9. How should business logic and persistence logic be separated under SRP?

A) Keep persistence logic inside the data entity class
B) Keep persistence logic in a Repository class and business logic in a Service class (Correct)
C) Merge them inside a unified manager

10. What does the term "low coupling" refer to in OOP design?

A) Making classes highly dependent on one another
B) Designing classes so that changes in one class do not impact other classes (Correct)
C) Using multiple inheritance hierarchies

21. Next Lesson Preview

In the next lesson, we will explore the Open/Closed Principle (OCP) to learn how to design software entities that are open for extension but closed for modification!