The Single Responsibility Principle (SRP) states that each class should have only one responsibility, meaning it should do one thing and do that thing well. A responsibility is a reason to change, so each class should have only one reason to change. If we group together the functions that need to change for the same reason, and separate out the things that change for other reasons, we can create a class that follows this principle.
If a class has multiple responsibilities, there is a likelihood that it is used in a greater number of places. When one responsibility is changed, not only do we run a higher risk of introducing defects into other responsibilities in the same class, but there is a greater number of other classes that might be impacted.
By following the single responsibility principle, if we need to change a particular responsibility, that change would be located in a single class. This is the way to create an orthogonal software system.
Applying this principle does not necessarily mean, as some posit, that each class should only have a single public method. Although it does reduce the size of classes, the goal is to have each class have a single responsibility. Fulfilling a single responsibility may require multiple public methods.
This principle is related to the SoC principle because, as concerns are separated from each other, it facilitates the creation of classes that have a single responsibility. Following the DRY principle also helps us to abide by the SRP. By removing duplicate code and placing it in a single location so that it can be reused, the classes that need the logic do not have to repeat it and therefore do not need to be responsible for it.
Let's take a look at an example of the SRP. It is written in C#, although you will get the idea even if you do not use that particular programming language. We have an email service that is responsible for sending out emails. There is a requirement to log information to a log file, so it also contains logic to open, write to, and close a log file on a file system:
public class EmailService : IEmailService
{
public SendEmailResponse SendEmail(SendEmailRequest request)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
SendEmailResponse response = null;
try
{
// Logic to send email
// Log info about sent email
LogInfo("Some info message");
}
catch (Exception ex)
{
// Log details about error
LogError("Some error message");
}
return response;
}
private void LogInfo(string message)
{
// Logic to write to file system for logging
}
private void LogError(string message)
{
// Logic to write to file system for logging
}
}
As you can see in this simple example, this class has more than one responsibility: sending out emails as well as handling the logging. This means that it has more than one reason to change. If we wanted to change how emails were sent out, or allow for the logging to target cloud file storage instead of a file on a local file system, both would require changes to the same class.
This violates the SRP. Let's refactor this class so that it is only responsible for one thing:
public class EmailService : IEmailService
{
private readonly ILogger _logger;
public EmailService(ILogger logger)
{
if (_logger == null)
throw new ArgumentNullException(nameof(logger));
_logger = logger;
}
public SendEmailResponse SendEmail(SendEmailRequest request)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
SendEmailResponse response = null;
try
{
// Logic to send email
// Log info about sent email
_logger.LogInfo("Info message");
}
catch (Exception ex)
{
// Log details about error
_logger.LogError($"Error message: {ex.Message}");
}
return response;
}
}
Now, the EmailService class is only responsible for sending out emails. The logic for logging has been abstracted out to an interface. This dependency is injected in through the class's constructor, and the implementation will be responsible for how logging works.
This class is now only responsible for a single thing and therefore only has one reason to change. Only changes related to the sending of emails will require modifications to this class. It no longer violates the SRP.