Single-responsibility principle

svg viewer

SRP: Splitting a non-SRP class

“A class should only have a single responsibility, that is, only changes to one part of the software's specification should be able to affect the specification of the class.”

The single-responsibility principle (SRP) states that each class, module, or function in your program should only do one job. In other words, each should have full responsibility for a single functionality of the program. The class should contain only variables and methods relevant to its functionality.

Classes can work together to complete larger complex tasks, but each class must complete a function from start to finish before it passes the output to another class.

Martin explained this by saying “a class should have only one reason to change”. Here the “reason” is that we want to change the single functionality this class pursues. If we do not want this single functionality to change, we will never change this class because all components of the class should relate to that behavior.

Therefore, we could change all but one class in the program without breaking the original class.

SRP makes it easy to follow another well-respected principle of OOP, encapsulation. It is easy to hide data from the user when all data and methods for a job are within the same single-responsibility class.

If you add a getter and setter method to a single-responsibility class, the class meets all criteria of an encapsulated class.

The benefit of programs that follow SRP is that you can change the behavior of a function by editing the single class responsible for it. Also, if a single functionality breaks, you know where the bug will be in the code and can trust that only that class will break.

This factor also helps with readability because you only have to read a class until you determine its functionality.

Implementation

Let's look at an example of how SRP can be applied to make our RegisterUser class more readable.

// does not follow SRP
public class RegisterService
{
    public void RegisterUser(string username)
    {
        if (username == "admin")
            throw new InvalidOperationException();

        SqlConnection connection = new SqlConnection();
        connection.Open();
        SqlCommand command = new SqlCommand("INSERT INTO [...]");//Insert user into database. 

        SmtpClient client = new SmtpClient("smtp.myhost.com");
        client.Send(new MailMessage()); //Send a welcome email. 
    }
}

The program above does not follow SRP because RegisterUser does three different jobs: register a user, connect to the database, and send an email.

This type of class would cause confusion in larger projects, as it is unexpected to have email generation in the same class as the registration.

There are also many things that could cause this code to change like if we make a switch in a database schema or if we adopt a new email API to send emails.

Instead, we need to split the class into three specific classes that each accomplish a single job. Here's what our same class would look like with all other jobs refactored to separate classes:

public void RegisterUser(string username)
{
    if (username == "admin")
        throw new InvalidOperationException();

    _userRepository.Insert(...);
    
    _emailService.Send(...);
}

This achieves the SRP because RegisterUser only registers a user and the only reason it would change is if more username restrictions are added. All other behavior is maintained in the program but is now achieved with calls to userRepository and emailService.

Related Tutorial
Follow Us
https://www.facebook.com/Rookie-Nerd-638990322793530 https://twitter.com/RookieNerdTutor https://plus.google.com/b/117136517396468545840 #
Contents +