The Single Responsibility Principle (SRP) states that "a class should have one, and only one, reason to change".
Play explanatory video
In simpler terms, each class or module should be responsible for a specific part of the functionality, and that responsibility should be entirely encapsulated by the class.
# Responsibility?
When we talk about responsibility, we are talking about roles, about stakeholders in a module or function, actors who require changes to the software. Responsibilities are basically families of functions that meet the needs of those actors.
When any of the roles involved in these responsibilities decide to change or add functionality, they will have to change that class. Increasing the probability of collision, complexity, and potential bugs.
In the following example, we have a class that affects several actors, several responsibilities:
class Employee { public function calculatePay() { // ... // Accounting } public function save() { // ... // IT } public function describeEmployee() { // ... // Human Resources } }
On one hand Accounting, on another IT, and on another HR. A change required by one of them will affect the others.
# Why is it important?
The disadvantage of a class or module having more than one responsibility—or better said, having more than one reason to change—is that when a change is introduced for one of the responsibilities, it can affect the functioning of others.
Maintainability
It is easier to maintain and modify the code.
Flexibility
You can change one part of the system without affecting others.
Understandability
Each part of the system can be understood in isolation.
# Symptoms of violation
We can identify when we are not respecting SRP:
- Class with many lines of code.
- Every time a change is made or functionality is added, it is necessary to touch many places.
- Mixing functionalities from different architectural layers.
- "Touching one part" causes "something else to break" in the same module or class.
Example
Suppose we have an application to manage products in a store.
Without Single Responsibility Principle:
A bad practice would be to have a class that handles both database operations and the business logic of products.
public class ProductManager { public void addProduct(Product product) { // Logic to add product to the database } public void calculateTotalStockValue() { // Logic to calculate total inventory value } }
// Here, ProductManager has multiple reasons to change, which violates SRP.
With Single Responsibility Principle:
We are going to separate the responsibilities into two distinct classes: one that handles the interaction with the database and another that handles the inventory calculations.
// ProductDB.java
public class ProductDB { public void addProduct(Product product) { // Handles only the database logic to add products } }
// InventoryCalculator.java
public class InventoryCalculator { public double calculateTotalStockValue() { // Handles only the logic to calculate the inventory value // Returns the calculated value return 0.0; } }
// Product.java (Entity)
public class Product { // Attributes like name, price, quantity, etc. private String name; private double price; private int quantity; // Constructor, getters and setters public Product(String name, double price, int quantity) { this.name = name; this.price = price; this.quantity = quantity; } // ... (other methods and attributes) }
Conclusions
By implementing SRP, ProductDB exclusively
handles database interaction, while InventoryCalculator
only handles inventory-related calculations.
This not only makes the code easier to maintain and extend but also helps avoid complicated errors that can arise when a class has too many responsibilities.
Summary
When we talk about a single responsibility, it doesn't mean that a class has only one method, nor a module that has only one class, but that it includes all the related functionality that can change together, that depends on the same actor.
A correct application of the Single Responsibility Principle makes our code flexible to change and less prone to errors.