The Single Responsibility Principle

David E Lares S
Nerd For Tech
Published in
4 min readOct 26, 2021

This is the first of the 5 rules of the SOLID principles.

The single responsibility aka SRP states that every software component should have one and only one responsibility. This component can be a class, a method, or even a module.

Instead of having a Swiss Army Knife that solves everything, you can have a bunch of single knives that can suit a single responsibility, only to cut.

The goal of the SRP is to defeat cohesion efficiently. In a more formal definition, cohesion is the degree to which the various parts of a software component are related.

A good analogy for this is when having a bunch of garbage in a place, then, this garbage is segregated and classified by the type of garbage. This segregation process can produce a high cohesion or the opposite.

Here’s a quick example of a possible actual solution in an app.

public class Square {  int side = 5;  public int calculateArea() {
return side * side;
}
public int calculatePerimeter() {
return side * 4;
}
public void draw() {
if (highResolutionMonitor) {
// render high resolution image
} else {
// render normal image
}
}
public void rotate(int degree) {
// rotate the image with the selected degree
}
}

This code presents 4 methods, but the calculateArea and the calculatePermiter are the ones that have a really tight level of cohesion, and the draw and rotate methods not so much.

If you remember, the goal is to achieve good cohesion, and we should always aim for high cohesion written in a component.

Both draw and rotate methods can be placed elsewhere, and “delegate” the responsibility for rendering functionality. So, please assume that both methods will be gone from the Square.java file and from the Square class definition.

This new code for rendering will be named SquareUI.java for the sake of simplicity, and here’s how it looks

public class SquareUI { public void draw() {
if(highResolutionMonitor) {
// render high resolution image
} else {
// render normal image
}
}
public void rotate(int degree) {
// rotate the image with the selected degree
}
}

The Square.java will now look like this.

public class Square { int side = 5; public int calculateArea() {
return side * side;
}
public int calculatePerimeter() {
return side * 4;
}
}

So.

  1. The Square.java is only present for the measurement of the squares
  2. The SquareUI.java is for rendering purposes.

This is the most simple example for demoing purposes. But this can apply to everything.

public class Student {private String studentId;
private Date studentDOB;
private String address;
public void save() {
String objectStr = MyUtils.serializeIntoAString(this);
Connection connection = null;
Statement stmt = null;
try {
Class.forName("com.mysql.jdbc.Driver");
DriverManager.getConnection("jdbc:mysql://localhost:3306", "MyDB", "root", "password");
stmt = connection.createStatement;
stmt.execute("INSERT INTO STUDENT VALUES (" + objectStr + ")");
} catch (Exception e) {
e.printStackTrace();
}
}
public String getStudentId() {
return studentId;
}
public void setStudentId(String studentId) {
this.studentId = studentId;
}
}

This code performs a database connection in the save method, this also grabs the data based on the started instance (OOP perspective).

This is pretty common to find inside ORM implementations. But of course, this can change and split into files with specific concerns.

Another interesting concept found here is Coupling, which is defined as the level of interdependency between various software components. The more tight the coupling is the more undesirable will be.

So, how is achieved?

Well, the save method just only performs the connection to the database, it just serializes the Student class into a persistent form, but the class attributes and the getStudentId and the setStudentId interacts with the data.

The Student class should ideally deal with related functionality, and not be made cognizant of the low-level details related to dealing with the backend interface.

So, again.

The refactoring will look like this for the Student.java class

public class Student {private String studentId;
private Date studentDOB;
private String address;
public void save() {
new StudentRepository().save(this);
}
public String getStudentId() {
return studentId;
}
public void setStudentId(String studentId) {
this.studentId = studentId;
}
}

Another file will be created, called StudentRepository.java will be for the backend instance

public class StudentRepository {public void save() {
String objectStr = MyUtils.serializeIntoAString(this);
Connection connection = null;
Statement stmt = null;
try {
Class.forName("com.mysql.jdbc.Driver");
DriverManager.getConnection("jdbc:mysql://localhost:3306", "MyDB", "root", "password");
stmt = connection.createStatement;
stmt.execute("INSERT INTO STUDENT VALUES (" + objectStr + ")");
} catch (Exception e) {
e.printStackTrace();
}
}
}

This helps of better adherence to the SRP

The Student class will handle the core student profile data and the StudentRespository for the backend/database operations.

I think at this point this whole principle becomes very clear.

Software is never dormant, it always keeps changing. If you have any reason to change, the frequency of changing goes up, and the number of bugs goes up as well.

Following SRP can lead to considerable savings on the Software Maintenance Costs.

Let’s continue with the Open-Closed Principle in the next post.

--

--