SOLID: Single Responsibility Principle – (SRP)

woman sitting on the floor using a laptop
Photo by Vlada Karpovich on Pexels.com

In the last post, we introduced the 5 important SOLID design principles that developers should be aware of. This post will talk about the first one: Single Responsibility Principle.

As the name suggests, Single Responsibility Principle (SRP) states that:

Every function, module or class should have responsibility over a single part of the functionality provided by the software.

Robert C Martin says “A class should have only one reason to change”.

The principle states that, if there are 2 reasons to change a class, the functionality should ideally be split into 2 separate classes. This is also related to the concepts of coupling and cohesion.

Single Responsibility Principle can be considered as the most important design principle one needs to adhere to, as everything else flows from this, in one way or the other.

Before moving on, let’s try to understand what do we stand to benefit if we follow the Single Responsibility Principle.

When we follow SRP,

  • Classes become smaller and cleaner
  • Code becomes easy to change, refactor, and extend
  • Classes become loosely coupled

Let’s look at some examples.

Example 1

We will start with a simple one. We have a User class, a simple Java POJO class.

User.java

public class User {
    private String firstName;
    private String lastName;
    public String getFirstName(){
        return firstName;
    }
    public String getLastName(){
        return lastName;
    }
    public String getName(){
        return firstName + ", " + lastName;
    }
}

Do you see anything wrong? It’s quite obvious that the User class was supposed to be a pure POJO class, with fields and their respective getters and setters. So, what is the responsibility of this class?

To hold a User’s fields, and expose getters and probably setters for these fields.

So, are we following Single Responsibility Principle here?

NO

We have added an extra responsibility to the class, to format and return the users name by manipulating its fields. And that also gives us 2 reasons to change this class.

  1. When the User data format/structure changes
  2. When the name format needs to be updated (May be we want to replace the “,” with a space sometime later, depending on a user’s preference.

So, how can we fix this? There could be several ways, and it depends on your specific use case. However, let’s see a simple trivial solution. Create a new class.

User.java

public class User {
    private String firstName;
    private String lastName;

    public String getFirstName(){
        return firstName;
    }

    public String getLastName(){
        return lastName;
    }
}

TextUtils.java

public class TextUtils {
    public static String getDisplayName(User user){
        return user.getFirstName() + ", " + user.getLastName();
    }
}

Example 2

Let’s review a class which fetches a list of users for you to display on a page.

UserAPI.java

public class UserAPI {
    public List<User> getActiveUsers(){
        //
        //
        List<User> users = // Make a GET Request and prepare the list of users
        saveToDatabase(users);
        return users;
    }

    private void saveToDatabase(List<User> users){
        // Insert list of users to database
    }
}

The class does the job, but also does something, which doesn’t seem to be its responsibility, i.e: saving the list of users to the database? UserAPI thus, has 2 responsibilities, clearly violating the Single Responsibility Principle.

And thus, we have more than 1 reason now to change the UserAPI class. Say for example, at some future point in time, we need to save the users in another database.

Here, there are 2 levels of violations.

  1. Class level
  2. Method level – getActiveUsers() – Think about it

We will not talk about a solution here, since it would again depend on your specific scenario.

What should we do?

We, as developers should always follow Single Responsibility Principle while designing and writing code. But how do we make sure that we are following this principle every-time we are writing code?

Frankly, it’s not always as easy as the examples that we have discussed. It also depends on your specific project and situation. There is no universal rule which you can adopt and follow while writing code.

However, a few warning signs should be kept in mind which could indicate that you might be already violating SRP.

  1. Large classes
  2. Large methods
  3. Too many parameters in constructor
  4. Too many parameters in methods
  5. Complex and confusing class and method names