What is the Single Responsibility Principle?

The Single Responsibility Principle (SRP) is the first of the SOLID principles, and makes up the S.

What are the SOLID principles?

The SOLID principles are a set of principles that guide developers in the designing of software. These ideas were first written by Robert C. Martin (“Uncle Bob”), he didn’t give them the famous SOLID mnemonic, rather he just laid out his five rules that governed how he wrote clean object-oriented code. They were designed to make code more readable, extendable and testable in any environment and any language. Robert’s books provide both anecdotal and statistical evidence of how his practices improved the delivery of software in the business’ he has worked for and the SOLID principles are a summarisation of the rules he followed.

A forewarning to these principles is to avoid needless complexity in your code. A large problem that OOP and “SOLID Code” suffers with is over-complexity. Some of the patterns and ideas and how they are applied have been long-term abused and should only really be used as they are needed. Don’t attempt to apply every single one of these principles in your plan before you have written a single line of code.

Single Responsibility Principle

The Single Responsibility Principle (SRP) defines a responsibility as “a reason to change”. A class should only ever have one reason to change.

That definition seems slightly hand-wavey at first and could mean a lot of things. Another way to approach the concept is considering the amount of jobs a part of code has. If a class, function or block of code has more than one job to do, then it is considered to have more than a single responsibility and “closely coupled”.

The reason for the usage of a reason to change is that when a closely coupled class changes in some way, then the changes can break the code in unexpected ways. Therefore the ultimate reason to adhere to SRP is to minimise the chance of having to go back and change your code.

A basic example to visualise the idea is a blogging system built in C#. The flow of creating a new “user” on this blogging website is to create a new user, add it into the database and send an email to the user to confirm their account.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class User
{
private readonly string _username;
private readonly string _password;
private readonly string _emailAddress;
private readonly Database _db;

public User(string username, string password, string emailAddress, Database db)
{
_username = username;
_password = password;
_emailAddress = emailAddress;
_db = db;
}

public void AddUser()
{
_db.Add(_username, _password, _emailAddress);
}

public void SendEmail(string message)
{
SmtpClient smtp = new SmtpClient();
smtp.Send(new MailMessage("[email protected]", _emailAddress));
}
}

This class ends up with a number of responsibilities:

  1. Representing a User
  2. Storing the User in a database
  3. Sending emails to the user

What we end with are multiple reasons to change this class: If the email service we use changes, if the database or process of storing users changes, and if the properties of a user changed.
A quick refactor to separate these responsibilities gives us:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class User
{
public string Username { get; }
public string Password { get; }
public string EmailAddress { get; }

public User(string username, string password, string emailAddress)
{
Username = username;
Password = password;
EmailAddress = emailAddress;
}
}

public class EmailService
{
private readonly SmtpClient _smtpClient;

public EmailService(SmtpClient smtpClient)
{
_smtpClient = smtpClient;
}

public void SendEmail(MailMessage message)
{
_smtpClient.Send(message);
}
}

public class DatabaseService
{
private readonly Database _db;

public DatabaseService(Database db)
{
_db = db;
}

public void AddUser(User user)
{
_db.Add(user.Username, user.Password, user.EmailAddress);
}
}

The email and database responsibilities were extracted out into their own classes, so these can be extended or modified as demand requires and the other classes need not be aware.