DEV Community

loading...

SOLID: Single Responsibility Principle

Victor Manuel Pinzon
Computer System Engineer specialized in software development and project management.
Updated on ・5 min read

Code quality is one of the angular pieces of software development, nonetheless, is also one of the most neglected ones. The current trend on software development is to focus on learning new languages, new paradigms and the new trendy technologies that everybody tweets on Twitter. Leaving aside the importance of good practices and learning on how to develop scalable and robust solutions with high code quality.

SOLID principles are vitally important in object-oriented programing (OOP) because it allows you to write programs which are maintainable, extendable and testable. However, it is very common to find experienced and non-experienced developers that haven’t heard of them. Of course, I wasn’t the exception to the rule. My passion for software development started in 2004, developing simple adding and subtracting programs in Pascal. Throughout the years I learned multiple languages, such as C, C++, Visual Basic, Visual Fox Pro, etc. In 2008 I learned Java and the OOP paradigm, but it wasn’t until 2014, that a workmate lent me the book “Clean Code” by Robert C. Martin. From this book, I learned about coding standards and best practices, but most importantly I learned about the SOLID principles.

Before reading “Clean Code”, 6 years went by where I developed professionally without knowing about SOLID principles. To be honest with you, there is a huge difference between the quality of my programs before and after having learned them. The most notorious improvement is that, now, I’m able to write applications that are easily maintainable and testable.

Believing that never is too late to learn, we will review each one of the principles.

SOLID Principles

S.O.L.I.D principles are 5 five foundational principles in object-oriented programming. These were developed by Robert C. Martin (Uncle Bob) in 2000. The main objective of SOLID principles is to eliminate bad practices in software design, as well as helping the developer to write maintainable, scalable, and testable code.

The SOLID principles are conformed by:

  • Single Responsibility Principle.
  • Open/Closed Principle.
  • Liskov Substitution Principle
  • Interface Segregation Principle.
  • Dependency Inversion Principle.

The term SOLID is an acronym for those five principles.

Single Responsibility Principle

Single Responsibility Principle (SRP) refers that a class, method or module must have just one responsibility. In other words, it must have just one reason to change. Sometimes we create classes, methods or modules that have too many responsibilities at once. For instance, sometimes you can have a class that has business logic, persistence logic, log register, all in the same class. This is a violation of SRP because the class is trying to do many things at once and a change in one of those responsibilities means all other responsibilities will be affected as well.

The main objective of SRP is the separation of responsibilities and scope reduction when you introduce a change.

Let’s see the following example:

package com.yourregulardeveloper.entities;

import java.math.BigDecimal;
import java.util.List;

public class Client {
    private String clientId;
    private String name;
    private String lastName;

    public Client(String clientId, String name, String lastName) {
        this.clientId = clientId;
        this.name = name;
        this.lastName = lastName;
    }

    /**
     * Calculates the subtotal invoice.
     * @param articles         List of articles bought by the client.
     * @return                 Invoice subtotal.
     */
    public BigDecimal calculateSubTotalInvoice(List<Article> articles){
        return articles
                .stream()
                .map(article -> article.getUnitPrice()
                        .multiply(new BigDecimal(article.getAmount)))
                .reduce(BigDecimal.ZERO, BigDecimal::add);

    }

    /**
     * Calculates the total invoice.
     * @param subtotal                  Invoice subtotal.
     * @param taxPercentage             Tax percentage.
     * @return                          Total invoice.
     */
    public BigDecimal calculateTotalInvoice(BigDecimal subtotal, BigDecimal taxPercentage){
        return subtotal.add(subtotal.multiply(taxPercentage));
    }

    public String getClientId() {
        return this.clientId;
    }

    public void setClientId(String clientId) {
        this.clientId = clientId;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}
Enter fullscreen mode Exit fullscreen mode

The class “Client” violates the SRP because it has more than one responsibility, which are:

  • The handling of the client entity throughout the application lifecycle. This responsibility is given by the attributes and methods related to the client’s info.
  • Invoice totals calculator. This responsibility is given by the methods calculateSubTotalInvoice() and calculateTotalInvoice().

The main, and only, responsibility of the class “Client” should be the handling of the client entity. A bad design introduced a second responsibility to the class. To solve this bad design we could implement SRP and separate the responsibilities into two different classes.

package com.yourregulardeveloper.entities;

public class Client {
    private String clientId;
    private String name;
    private String lastName;

    public Client(String clientId, String name, String lastName) {
        this.clientId = clientId;
        this.name = name;
        this.lastName = lastName;
    }

    public String getClientId() {
        return this.clientId;
    }

    public void setClientId(String clientId) {
        this.clientId = clientId;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}
Enter fullscreen mode Exit fullscreen mode
package com.yourregulardeveloper.invoice;

import java.math.BigDecimal;
import java.util.List;

public class InvoiceCalculator {

    /**
     * Calculates the subtotal invoice.
     * @param articles         List of articles bought by the client.
     * @return                 Invoice subtotal.
     */
    public BigDecimal calculateSubTotalInvoice(List<Article> articles){
        return articles
                .stream()
                .map(article -> article.getUnitPrice()
                        .multiply(new BigDecimal(article.getAmount)))
                .reduce(BigDecimal.ZERO, BigDecimal::add);

    }

    /**
     * Calculates the total invoice.
     * @param subtotal                  Invoice subtotal.
     * @param taxPercentage             Tax percentage.
     * @return                          Total invoice.
     */
    public BigDecimal calculateTotalInvoice(BigDecimal subtotal, BigDecimal taxPercentage){
        return subtotal.add(subtotal.multiply(taxPercentage));
    }

}
Enter fullscreen mode Exit fullscreen mode

Now we have the class “Client” and the class “InvoiceCalculator”. Each one of those classes have just one responsibility or just one reason to change. SPR allowed the class “Client” to have just the responsibility of handling the client entity and the class “InvoiceCalculator” to just have the responsibility of calculating the invoice totals. Both classes are easier to read, to maintain and to test.

Single Responsibility Principle on methods

SRP can be applied on methods, classes or modules. I make emphasis on methods because is very common to develop methods that do too many things at once. I’ve seen some huge 500 lines methods that have too many responsibilities at once. For instance, is common to find methods that record on logs, do some calculations and finally they persist the result of those calculations in the database. The problem with these types of methods is that besides they are difficult to maintain and difficult to introduce changes, they also are difficult to test.

SRP allows to write methods that have just one responsibility without caring about the size of it, just caring about the method having one reason to change.

Single Responsibility Principle bad practices

Sometimes the poor implementation of SRP can be counterproductive. For instance, if the SRP implementation is exaggerated you could end up writing classes with just one method, which could be wrong. We must remember that SRP dictates that a coding unit (method/class/module) must have just one reason to change. We must also remember that the correct implementation of SPR allows us to group different code units with one responsibility. It doesn’t dictate the number of methods in a class or the number of lines in a method.

SRP helps us to separate concerns and responsibilities and is key to write scalable, maintainable, and testable code. It allows us to eliminate bad practices, such as: implementing classes and methods that do many things at once.

If you like to read more about SRP, you can have a look at Uncle Bob's Blog.

In the next post, we will talk about the Open/Closed Principle.

You can follow me on Twitter Twitter.

Discussion (2)

Collapse
yuriuss profile image
Yuriuss

Good article! Waiting for next.

Collapse
victorpinzon1988eng profile image
Victor Manuel Pinzon Author

I’ve just posted the second SOLID article.

Forem Open with the Forem app