Single responsibility principle

The single responsibility principle (SRP) states that a software component (in general, a class) must have only one responsibility. The fact that the class has a sole responsibility means that it is in charge of doing just one concrete thing, and as a consequence of that, we can conclude that it must have only one reason to change. It is one of 5 famous SOLID principles.

A class should have only one reason to change.

There is another way of looking at this principle. If, when looking at a class, we find methods that are mutually exclusive and do not relate to each other, they are the different responsibilities that have to be broken down into smaller classes. Remember that the smaller classes are always better.

1. Motivation

If we have to make modifications to a class, for different reasons, it means the abstraction is incorrect, and that the class has too many responsibilities. Basically we want to avoid in all cases is having objects with multiple responsibilities (often called god-objects, because they know too much, or more than they should). These objects group different (mostly unrelated) behaviors, thus making them harder to maintain.

2. Single responsibility principle example

To understand single responsibility principle real world example, we can look at the JDK code and other popular libraries such as Log4J, Spring framework etc. Log4J code has different classes with logging methods, different classes are logging levels and so on. In Spring framework, classes are really very small and usually perform only one or two related actions.

Let’s take another example of our own to better understand what is single responsibility principle.

2.1. Problem

To understand the SRP principle, let’s assume we have working on an application which involve working with employees. We have an interface IEmployeeStore and it’s implementation EmployeeStore which have following methods.

public interface IEmployeeStore 
{
	public Employee getEmployeeById(Long id);
	
	public void addEmployee(Employee employee);
	
	public void sendEmail(Employee employee, String content);
}
public class EmployeeStore implements IEmployeeStore 
{
	@Override
	public Employee getEmployeeById(Long id) {
		return null;
	}
	
	@Override
	public void addEmployee(Employee employee) {
		
	}

	@Override
	public void sendEmail(Employee employee, String content) {		
	}
}

Above class seems good on any normal application. using EmployeeStore, are able to get/add employees and send email to them.

Now suppose after product release, we got requirement that email content can be of two types i.e. HTML and text. Above class supprt only text content. What you will do?

One way to solve this issue is create another method sendHtmlEmail() – but what happens when we are asked to support different protocols for sending emails for both content types. Overall class will look very ugly and difficult to read and maintain.

And there is always a chance that during modification, some developer can change the logic used for get/add employee methods if they are shared.

2.2. Solution is SRP Principle

To solve this issue, we must pull out the email capability to separate interface and classes which specifically handle only email related functionality. This way, we are sure that other features are not impacted.

Based on our assumptions, we can abstract out two interfaces IEmailSender and IEmailContent. First interface is responsible for email sending process and second interface is responsible for passing the email content and it’s type.

The refactored classes are given below.

public interface IEmployeeStore 
{	
	public Employee getEmployeeById(Long id);
	
	public void addEmployee(Employee employee);
}
public class EmployeeStore implements IEmployeeStore 
{
	//inject in runtime
	private IEmailSender emailSender;
	
	@Override
	public Employee getEmployeeById(Long id) {
		return null;
	}
	
	@Override
	public void addEmployee(Employee employee) {
	}
}
public interface IEmailSender 
{
	public void sendEmail(Employee employee, IEmailContent content);
}
public class EmailSender implements IEmailSender
{
	@Override
	public void sendEmail(Employee employee, IEmailContent content) {		
		//logic
	}
}
public interface IEmailContent {
	
}
public class EmailContent implements IEmailContent 
{
	private String type;
	private String content;
}

Now if we want to change the email functionality, we will change EmailSender class only. Any change to employee CRUD operations will happen in EmployeeStore only. Any change in one capability will not change other one by mistake. They are not more easy to read and maintain as well.

3. Benefits

3.1. Easy to understand and maintain

When the class only does “one thing”, its interface usually has smaller number of methods (and member variables) that are fairly self explanatory. It makes the code easier to read and understand.

When there is a need to change the application behavior, changes related to the class’s responsibility are fairly isolated. It reduces the chance of breaking other unrelated areas of the software. It makes the code easier to maintain.

3.2. Improved usability

If a class has multiple responsibilities and it is required to be used in other parts of the application for using a certain responsibility, it may adversely expose other responsibilities which may not be desired. It can lead to undesired behavior in the application e.g. security and data privacy issues.

If the class follows the SRP principle strictly then unnecessary functionality will not be exposed and it makes the class to be more usable without fearing adverse effects.

4. Conclusion

The single responsibility principle design pattern has a hugely positive impact on the adaptability of code. Compared to equivalent code that does not adhere to the principle, SRP-compliant code leads to a greater number of classes that are smaller and more directed in scope.

The SRP is primarily achieved through abstracting code behind interfaces and delegating responsibility for unrelated functionality to whichever implementation happens to be behind the interface at run time.

Read More:

Separation of concerns

Was this post helpful?

Join 7000+ Fellow Programmers

Subscribe to get new post notifications, industry updates, best practices, and much more. Directly into your inbox, for free.

2 thoughts on “Single responsibility principle”

  1. I guess the “IEmailContent content” inside IEmailSender.java and EmailSender.java should be EmailContent content.
    Like below:

    IEmailSender.java
    public interface IEmailSender
    {
    public void sendEmail(Employee employee, EmailContent content);
    }
    EmailSender.java
    public class EmailSender implements IEmailSender
    {
    @Override
    public void sendEmail(Employee employee, EmailContent content) {
    //logic
    }
    }

    Please correct if I am wrong: This code is driving me crazy! I am a n00b trying to understand it.

    Reply

Leave a Comment

HowToDoInJava

A blog about Java and its related technologies, the best practices, algorithms, interview questions, scripting languages, and Python.