The Single Responsibility Principle
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.
- The
Square.java
is only present for the measurement of the squares - 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.