DEV Community

Cover image for SOLID: Single Responsibility Principle With Examples
Gopi Gorantala
Gopi Gorantala

Posted on • Edited on

SOLID: Single Responsibility Principle With Examples

In this article, you will learn about the following:

  1. What is SRP?
  2. Importance of SRP in software development
  3. Strengths
  4. Weaknesses
  5. Twitter Registration

What is SRP?

The Single Responsibility Principle (SRP) is one of the five SOLID design principles that guide software development.

Definition: A class or module should have only one reason to change.

The principle states that a class should have only one reason to change and one responsibility. This principle is intended to promote modularity and help developers create easier code to understand, modify, and maintain.

In essence, the SRP states that each class should have a single, well-defined responsibility and that responsibility should be encapsulated within that class.

This means that a class should not have multiple responsibilities, as this can make it harder to understand and modify. By following the SRP, developers can create more maintainable and flexible code that is easier to work with over time.

The SRP is a fundamental principle in object-oriented programming, and it can significantly impact the quality and effectiveness of software development. We will explore the SRP in more detail, including how it works, why it is important, and how to apply it effectively in Java programming.

Importance of SRP in software development

There are several reasons why SRP is important in software development:

  1. Enhances code readability: Reading and understanding the code becomes easier when each class or module has a single responsibility. This helps developers quickly understand the purpose of the class or module and its relationship with other parts of the system.
  2. Increases code maintainability: By breaking down complex functionality into smaller, more focused modules, SRP enables developers to make changes to the code more easily without affecting other parts of the system. This means that maintenance and troubleshooting of the code become less time-consuming and costly.
  3. Facilitates code reuse: Code that adheres to SRP is often more modular and reusable. This means developers can easily reuse the code in other parts of the system or projects.
  4. Improves system scalability: Maintaining a single responsibility for each class or module becomes increasingly important as the codebase grows. SRP ensures that the codebase remains scalable and can easily accommodate changes or new features without impacting the rest of the system.

Overall, adhering to the Single Responsibility Principle improves the quality and maintainability of the codebase, making it easier to manage, test, and deploy.

Advantages

The advantages of following the Single Responsibility Principle (SRP) include the following:

  • Better code organization and maintainability.
  • Improved readability and understanding of code.
  • Easier debugging and testing of code.
  • A higher degree of code reusability.
  • Facilitation of parallel development and implementation of new features.
  • Ability to make changes to code with less risk of introducing bugs.

Disadvantages

Some of the disadvantages of the Single Responsibility Principle (SRP) include the following:

  • Increased complexity, as a system may require more classes to implement the same functionality.
  • Potential for over-engineering, leading to too much abstraction and unnecessary code.
  • Difficulties in determining the appropriate granularity of responsibilities.
  • Challenges in balancing the trade-off between separating responsibilities and maintaining performance.

When programmers try to add features or new behaviour to a software product, they frequently integrate everything within the current class. When something needs to be changed later, due to the complexity of the code, the code refactor process is extremely time-consuming and tedious.

The Single Responsibility Principle helps us create simple classes that perform just one task. This helps make modifications or adding extensions to the existing code much more effortless.

Twitter Registration Using Single Responsibility Principle

We design Twitter registration software with the help of the single responsibility principle that contains a notification service, database repository, account service, and execution class.

Let's implement the first design principle on Twitter registration flow.

🔐 Signup process on Twitter

Consider a use case where a user wants to register with Twitter. The steps Twitter takes to register are user are:

  1. Twitter asks users to register with signup forms.
  2. Twitter stores the user object in their database, which contains User details - email, name, password, and other metadata etc.
  3. Twitter sends a welcome message to the user.

Let us declare a class that does the above steps.

public class TwitterRegistration {
    public void register() {
        // step 1
        System.out.println("Fill signup form");

        // step 2
        System.out.println("Store account details in database");

        // step 3
        System.out.println("Send a welcome message");
    }
}
Enter fullscreen mode Exit fullscreen mode

We are creating an account on Twitter, and the three steps should be handled separately. But the above class declared them all in a single TwitterRegistration class. Isn't this a violation of SRP?

Also, step 2 of storing an object in the database requires additional work to open a connection with the database, authenticate the handshake, and store the user object. This is insertion logic and should be handled separately.

The third step is sending out a welcome message to the user, which should be handled by NotificationService, separately.

Using the SRP principle, we divide the above TwitterRegistration class into three different classes, each having a single and only one responsibility.

Refactoring for SRP

Refactoring TwitterRegistration the class gives:

// Notification Service
class NotificationService {
    public void sendNotification() {
        // step 3
        System.out.println("Send out welcome message");
    }
}
Enter fullscreen mode Exit fullscreen mode
// Database handshakes
class AccountRepository {
    public void createUser() {
        // step 2
        System.out.println("🔐 Auth Success!");
        System.out.println("Store user data into database");
    }
}
Enter fullscreen mode Exit fullscreen mode
// Account Registration
class TwitterAccountRegister {
    public void registerUser() {
        // step 1
        System.out.println("fill account internal details");
    }
}
Enter fullscreen mode Exit fullscreen mode

Finally, after refactoring the above classes. We first allow the TwitterAccountService to create a couple of objects for AccountRepository and NotificationService to register users with Twitter.

// Execution Class or Main class
public class TwitterAccountRegister {
    public static void main(String[] args) {
        TwitterAccountService service = new TwitterAccountService();
        service.registerUser();
    }
}

// Account Registration Service
class TwitterAccountService {
    AccountRepository repository = new AccountRepository();
    NotificationService notificationService = new NotificationService();

    public void registerUser() {
        // step 1
        System.out.println("fill account internal details");

        repository.createUser();
        notificationService.sendNotification();
    }
}

// Notification Service
class NotificationService {
    public void sendNotification() {
        // step 3
        System.out.println("Send out welcome message");
    }
}

// Database handshakes
class AccountRepository {
    public void createUser() {
        // step 2
        System.out.println("🔐Signup Success!! Registered");
        System.out.println("Store user data into database");
    }
}

/*
Outputs:
fill account internal details
🔐Signup Success!! Registered
Store user data into database
Send out welcome message
*/
Enter fullscreen mode Exit fullscreen mode

In above TwitterAccountService is doing all three tasks. The primary responsibility is to fill in account details in account details and delegate the other responsibilities to other classes.

Finally, we know many teams work on the same software product.
By following the SRP principle, if we see file changes in Github for a file named TweetAnalytics, we can be sure that the changes incorporated are related to analytics. This helps version control with ease.

Conclusion

In conclusion, the Single Responsibility Principle (SRP) is a software design principle that states that every class should have only one reason to change.

Following SRP makes code easier to understand, maintain, and extend. It reduces the risk of introducing bugs and makes it easier to test individual components in isolation.

SRP encourages the separation of concerns, making the code more modular and scalable. This principle is one of the five SOLID principles of object-oriented design and is an important aspect of creating clean, maintainable, and scalable code.

You will get the complete written notes @ https://www.ggorantala.dev/

Top comments (11)

Collapse
 
titaniumcoder477 profile image
James Wilmoth

Nice breakdown, thanks!

Collapse
 
krishna_bhaskar_2d8c2dd1c profile image
Bhaskar L

Nice article.

Collapse
 
catsarebetter profile image
Hide Shidara

Great stuff! solid!

Collapse
 
david_kariuki profile image
David Kariuki

Great article

Collapse
 
roksoftwaretr profile image
Rok Software

thank you :)

Collapse
 
devlinaung profile image
Lin Aung

Good Article, thanks!

Collapse
 
nimmneun profile image
Stefan

Sometimes, overly simplified examples can lead to adopters creating a bit of a mess in larger projects. E.g. having a dozen different services with a single public method each for password reset, registration, deletion, login, password update, profile update, etc ...

And naming things is hard sometimes. People name things differently. What's the difference between this ForgotPassword component and the ResetPassword component? Oh there's also a PasswordReset component here? Did we implement the same thing N times?

And User/Account stuff is still fairly simple and well understood. It does get more messy and out of hand in other areas.

Personally I tend hide/aggregate things through a facade nowadays. Similar to how you would do it with a public package/lib you publish. You only expose one (best case xD) or very few classes/functions to a dev using your package. And the package hopefully does one thing well e.g. Handle User/Account related stuff.

Just don't have me type UserService and my IDE suggest 50 different classes 😅

Collapse
 
pabzt profile image
Daniel Stoll

yet another developer writing about SRP and didn't understand it...

The common misconception about the Single Responsibility Principle is that it means a class should only do one thing or have only one functionality. Under this interpretation, developers often oversimplify their classes, leading to a large number of classes, each performing a very specific, narrow function. This could lead to an overly fragmented system which can be difficult to navigate and maintain.

However, Uncle Bob explains that the SRP is not about classes doing one thing, but rather about classes having one, and only one, reason to change. That reason to change is tied to a single actor or a single user group who would request that change. As such, the responsibility is defined in terms of the actor, not the functionality.

Here's a quote from "Clean Architecture" that clarifies the SRP:

"Gather together the things that change for the same reasons. Separate those things that change for different reasons."

This is a much more nuanced interpretation and is often more practical in real-world software development. A single class can have multiple methods and properties, as long as they all would change for the same reason, i.e., the same actor.

Collapse
 
tandrieu profile image
Thibaut Andrieu

Yet another person who created an account for the sole purpose of contradicting the author. Cunningham's law is strong with you man 😉.
Anyway, I agree with you.

Collapse
 
ggorantala profile image
Gopi Gorantala • Edited

@pabzt, check out this example - ggorantala.dev/srp-examples/.

Collapse
 
zoongit profile image
zoongit

I think what he meant is the implementation should use interfaces.
Check out this Single Responsibility Principle | Object Oriented Design
https://www.oodesign.com/single-responsibility-principle