You are here

SOLID Principles : The Single Responsibility Principle

SOLID Principles : The Single Responsibility PrincipleAlmost everywhere when we talk about delivery of a product, the first step comes in mind is it’s design. The more focus we put into the design, the better the product will look. Every design has some design principles that need to be followed while designing a product.  Hence, design principles have a crucial role in any product delivery. Design Principles help teams with decision making. In this article, We will discuss about ‘SOLID Principles: The Single Responsibility Principle’.

A few simple principles or valuable questions can guide our team towards taking relevant decisions. SOLID Principles are the set of five principles used to design a software. In fact, the word ‘SOLID’ is the acronym for the set of five principles that contains the first letter of each principle.

Robert Martin (Uncle Bob) has  introduced the SOLID Design Principles. Let’s discuss SOLID Principles : The Single Responsibility Principle in detail and related concepts.

Design Principles vs Design Patterns

Design principles are a set of generalized pieces of advice or proven good coding practices that are used as rules of thumb when making design choices. They’re a bit similar concept to design patterns. The main difference is that Design Principles are more abstract and generalized. They are kind of high-level pieces of advice, often applicable to many different programming languages or even different paradigms.

Design Patterns are also abstractions or generalized good practices, but they provide much more concrete and practical low-level advice. However, they are related to entire classes of problems rather than just generalized coding practices.

Design Principles encourage us to create more maintainable, understandable, and flexible software. Consequently, as our applications grow in size, we can reduce their complexity and save ourselves a lot of headaches further down the road!

Software Design is not perfect if it is executed in a single attempt. There is always a possibility to change it and make it even better. Furthermore, your software must be able to keep up with the changing needs of your customer.

What is Code Rot?

When developers work on design without using structured design principles, they can create long-lasting problems for future developers working on the project, and limit the potential success for the application they’re developing. These issues are commonly referred to as “code rot.”

How can we identify future Code Rot ?

Below signs probably indicate code rot to come:

Rigidity 

Small change causes the entire system to rebuild.

Fragility 

Changes to one module causes other unrelated modules to misbehave.

Immobility 

A module’s internal components cannot be extracted and reused in new environments. For example, if an application’s login module cannot be used in entirely different system then this module is immobile, caused by couplings and dependencies between different modules. The strategy is to decouple central abstractions from low-level details, like a particular database schema or UI implementation (web, desktop) or specific frameworks.

Viscosity 

When building and testing are difficult to perform and take a long time to execute. When even a simple change is costly to make, and requires you to make changes in multiple places/levels.

What are SOLID Principles?

In the world of object-oriented programming (OOP), there are many design guidelines, patterns or principles. Five of these principles are usually grouped together and are known by the acronym SOLID. While each of these five principles describes something specific, they overlap as well, such that adopting one of them implies or leads to adopting another.

S ⇒ stands for Single Responsibility Principle(SRP)

O ⇒ stands for Open Closed Principle(OCP)

L ⇒ stands for Liskov’s Substitution Principle(LSP)

I ⇒ stands for Interface Segregation Principle(ISP)

D ⇒ stands for Dependency Inversion Principle(DIP)

The SOLID principles were first conceptualized by Robert C. Martin in his 2000 paper, Design Principles and Design Patterns. These concepts were later built upon by Michael Feathers, who introduced us to the SOLID acronym. And in the last 20 years, these 5 principles have revolutionized the world of object-oriented programming, changing the way that we write software.

Besides monolithic apps, you can also apply SOLID design principles to microservices where you can treat each microservice as a standalone code module.

When you use all the principles of S.O.L.I.D in a combined manner, it becomes easier for you to develop software that can be managed easily. The other features of using S.O.L.I.D are:

  • It avoids code smells.
  • Quickly refactor code.
  • Can do adaptive or agile software development.

Now it’s time to understand SOLID Principles in much detail with the help of various examples.

What are the benefits of using SOLID Design Principles?

Here are some of the most important purposes of following SOLID Design Principles in our code.

1) To make it easier to quickly extend the system with new functionality without breaking the existing ones. If we don’t do implementation of a new feature in the application sensibly, It may even affect the existing functionality and cause unintentional issues.

2) To make the code easier to read and understand. Thus, spend less time figuring out what it does. If our code doesn’t follow the SOLID design principles, we generally take more time to understand what it does. The SOLID principle approach ensures that your code is relatively easier to read and understand.

3) To make the code more maintainable.

4) Every software needs upgradation time to time. Therefore, we need to build applications while keeping in mind the possibility of future changes. It is quite effortless to refactor your code with the SOLID principles.

5) If we apply the SOLID principles in the code, it will also ensure an easy debugging process as a whole.

What is Single Responsibility Principle (SRP) ?

Now It’s time to discuss about ‘SOLID Principles : The Single Responsibility Principle’. This Principle says that there should never be more than one reason for a class to change. A class should be focused on a single functionality, address a specific concern. This means that every class, or similar structure, in your code should have only one job to do. Everything in the class should be related to that single purpose, i.e., be cohesive. It does not mean that your classes should only contain one method or property.

The more responsibilities your class has, the more often you need to change it. If your class implements multiple responsibilities, they are no longer independent of each other. Therefore, you will need to change your class more often. Consequently, it will have more side-effects, and requires a lot more work than it should have. So, it’s better to avoid these problems by making sure that each class has only one responsibility.

Suppose you built your software over a longer period and you need to adapt it to ongoing change requests. You might feel the easiest and fastest approach is adding a method or functionality to your existing code instead of writing a new class or component. But that often results in classes with more than responsibility and makes it more and more difficult to maintain the software. You can avoid these problems by asking a simple question before you make any changes:

What is the responsibility of your class/component/microservice?

If your answer includes the word “and”, you’re most likely breaking the Single Responsibility Principle.

Then it’s better to take a step back and rethink your current approach. There is most likely a better way to implement it. For example, let’s understand it with the help of codes.

Example#1: Code that violates Single Responsibility Principle

Suppose we are writing a java application for a hiring consultancy. We will create a Resume class that lets consultant get & set the technology & years of experiences in each resume, and search the resume from the repository.

public class Resume {

     String technology;
     Integer yearsOfExperience;
  
     public String getTechnology() {

           return technology;
     }

     public void setTechnology(String technology) {

           this.technology = technology;
     }

     public Integer getYearsOfExperience() {

           return yearsOfExperience;
     }

     public void setYearsOfExperience(Integer yearsOfExperience) {

           this.yearsOfExperience = yearsOfExperience;
     }

     public void searchResume() {

           //logic to search resume goes here
     }

}

However, the above code violates the Single Responsibility Principle, as the Book class has two responsibilities. First, it sets the properties(technology and yearsofExperience) related to the Resume. Second, it searches for the resume in the repository. The setter methods change the Resume object, which might cause problems when we want to search the same resume in the repository.

Example#1: Code that follows the Single Responsibility Principle

In order to apply the Single Responsibility Principle, we need to decouple the two responsibilities. In the refactored code, the Resume class will only be responsible for getting and setting the properties of the Resume object. For example, below code demonstrates the concept.

public class Resume {

     String technology;
     Integer yearsOfExperience;   

     public String getTechnology() {

           return technology;
     }

     public void setTechnology(String technology) {

           this.technology = technology;
     }

     public Integer getYearsOfExperience() {

           return yearsOfExperience;
     }

     public void setYearsOfExperience(Integer yearsOfExperience) {

           this.yearsOfExperience = yearsOfExperience;
     }
}

Then, we create another class called RepositoryView that will be responsible for searching the resume. We move the searchResume() method here and reference the Resume class in the constructor.

public class RepositoryView {

     Resume resume;

     public RepositoryView(Resume resume) {

           this.resume = resume;
     }
    
     public void searchResume() {

           //logic to search resume goes here
     }
}

On the UML diagram below, you can see how the architecture changed after we refactored the code following the Single Responsibility Principle. We split the initial Resume class that had two responsibilities into two classes, each having its own single responsibility.

Class Diagram of Code 

SOLID Principles : The Single Responsibility Principle

Example#2: Code that violates the Single Responsibility Principle

Let’s consider another example to understand it more as in the below code:

public class Employee {

     public Double calculatePay() {...}

     public void saveEmployee() {...}

     public void getEmployeeReport() {...}

}

How many responsibilities?

The correct answer is three. 🙂

Here we have 1) calculation logic 2) database logic and 3) reporting logic. All mixed up within one class. If you have multiple responsibilities combined into one class, it might be difficult to change one part without breaking others. Mixing responsibilities also makes the class harder to understand and harder to test. It will also decrease the cohesion. The easiest way to fix this is to split the class into three different classes, with each having only one responsibility: database access, calculating payment and reporting, all separated.

Next question, how many reasons to change?

Again, the correct answer is three. ;-).

There are 3 reasons for this class to change. 1) Any change in payment calculation. 2) Any change in logic of saving employee into the database. 3) Any change in reporting logic. If you think in a broad way, in order to make changes in one part, we may also have to change all parts unnecessarily.

Example#2: Code that follows the Single Responsibility Principle

We will split the Employee class into three classes in order to follow the Single Responsibility Principle. For example, below code demonstrates the concept.

public class EmployeePayment {

     public Double calculatePay() {

           // logic to calculate employee payment

     }
}
public class EmployeeRepository {

     public void saveEmployee() {

           // logic to save employee object

     }  
}
public class EmployeeReport {

     public void getEmployeeReport() {

           // logic to generate employee report
     }   
}

Now we can call these methods from the respective classes wherever it is required. Don’t get confused with the fact that a class will have only one method to follow SRP. It can have many supporting methods, but they should not change the responsibility of the class.

Other Examples 

Imagine there is a class which performs following operations.

  • Connect to a database
  • Read some data from database tables
  • Finally, write it to a file.

Have you imagined the scenario? Here the class has multiple reasons to change, and few of them are the modification of file output, new data base adoption. When we are talking about single responsibility, we would say, there are too many reasons for the class to change; hence, it doesn’t fit properly in the single responsibility principle.

For example, let’s consider an Automobile class which can start or stop itself but the task of washing belongs to the CarWash class. A Book class has properties to store its own name and text. But the task of printing the book must belong to the BookPrinter class. The BookPrinter class might print to console or another medium but such dependencies are removed from the Book class.

EntityManager

You can find lots of examples of all SOLID design principles in an open source software and most well-designed applications. Such as your Java persistence layer and the popular frameworks and specifications, which you most likely used to implement it.

One of them is the Java Persistence API (JPA) specification. It has one, and only one, responsibility: Defining a standardized way to manage data persisted in a relational database by using the object-relational mapping concept. The EntityManager interface provides a set of methods to save, update, remove and read entities from a relational database. Its responsibility is to manage the entities that are associated with the current persistence context.

If you put more than one functionality in one class in Java it may introduce coupling between two functionalities. Even if you change one functionality, there is a chance that you broke another functionality. Therefore, it will require another round of testing to avoid any surprise on the production environment.

What is the benefit of the Single Responsibility Principle?

Let’s discuss How does this principle help us to build better software? i.e. a few of its benefits.

Easier to Test

A class with one responsibility will have far fewer test cases which results in very less efforts in testing.

Lower coupling

Less functionality in a single class will have fewer dependencies. Hence, it will lower the coupling.

Better Organization

Smaller, well-organized classes are easier to be searched by a first-time code reader than bigger ones.

Easier to implement

Classes, software components and microservices that have only one responsibility are much easier to explain, understand and implement than the ones that provide a solution for everything. This reduces the number of bugs, improves your development speed, and makes your life as a software developer a lot easier.

Easier to maintain

By making sure that your class have only one responsibility, you can save a lot of efforts in developing the application and create a more maintainable architecture.

That’s all about ‘SOLID Principles : The Single Responsibility Principle’. Moreover, Wikipedia defines this Principle like this. In order to learn the most commonly used design principles, kindly visit article on ‘OOPs Design Principles‘. Apart from ‘SOLID Principles : The Single Responsibility Principle’, we have discussed other principles as separate articles respectively. Furthermore, Should you like to learn Design patterns, you may visit our articles on Design Patterns in Java.

Links to Other Design Principles

Open Closed Principle(OCP)

Liskov’s Substitution Principle(LSP)

Interface Segregation Principle(ISP)

Dependency Inversion Principle(DIP)

close

One thought on “SOLID Principles : The Single Responsibility Principle

  1. Thanks for writing such awesome articles on the SOLID Design Principle. I loved reading all of them. The definition is explained to the point in a very simple way. Code examples are good as well. Hat’s off to the writer @devs5003

Leave a Reply

Top