Single Responsibility Principle¶
A class should have one, and only one, reason to change.
This principle states that a class must be specialized in a single subject and have only one responsibility within the software, that is, the class must have a single task or action to perform.
A class should have only one responsibility, it cannot do more than it was created to do.
- "Every Software Component should have only one responsibility."
A software component can be understood as a class, function, method, or even a module.
It's very common when we are developing a software to give a class more than one responsibility and end up creating classes that do it all — God Class*. At first, this may seem efficient, but as the responsibilities end up getting mixed up, when there is a need to make changes to this class, it will be difficult to modify one of these responsibilities without compromising the others. Every change ends up being introduced with a certain level of uncertainty in our system — especially if there are no automated tests!
*God Class: In object-oriented programming, a class that knows too much or does too much.
Bad code:
/** Before Refactoring **/
// Too Many Responsibilities
// - Measurements of squares
// - Rendering images of squares
class Square {
int side = 5;
public int calculateArea() {
return side * side;
}
public int calculatePerimeter() {
return side * 4;
}
public void draw() {
if (highResolutionMonitor) {
// Render a high resolution image of a square
} else {
// Render a normal image of a square
}
}
public void rotate(int degree) {
// Rotate the image of the square clockwise to
// the required degree and re-render
}
}
The Square class has two responsibilities, measuring the area of the square and rendering the image to the screen.
Refactoring:
/** After Refactoring **/
// Responsibility: Measurements of squares
// After Refactoring
class Square {
int side = 5;
public int calculateArea() {
return side * side;
}
public int calculatePerimeter() {
return side * 4;
}
}
// Responsibility: Rendering images of squares
class SquareUI {
public void draw() {
if (highResolutionMonitor) {
// Render a high resolution image of a square
} else {
// Render a normal image of a square
}
}
public void rotate(int degree) {
// Rotate the iamge of the square clockwise to
// the required degree and re-render
}
}
Now Square calculates the area and SquareUI handles the rendering.
calculateArea()
andcalculatePerimeter()
are related to the square calculation;draw()
androtate()
is related to rendering;
Example 2:
// Responsibility 1: Handle core student profile data
// Responsibility 2: Handle Database Operations
class Student {
private String studentId;
private String address;
public void save() {
// Serialize object into a string representation
String objectStr = MyUtils.serialzieIntoAString(this);
Connection connection = null;
Statement stmt = null;
// We are using MYSQL
// What if I want to use another database?
// This is why Tight Coupling is bad practices for prgramming
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/MyDb", "root", "password");
stmt = connection.createStatement();
stmt.execute("INSERT INTO STUDENTS VALUES (" + objectStr + ")");
} catch (Exception e) {
e.printStackTrace();
}
}
public String getStudentId() {
return studentId;
}
public void setStudentId(String studentId) {
this.studentId = studentId;
}
}
Refactoring:
/** After Refactoring **/
// Responsibility: Handle Database Operations
public class StudentRepository {
public void save(student Student) {
// We are using MYSQL
// What if I want to use another database?
// This is why Tight Coupling is bad practices for prgramming
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/MyDb", "root", "password");
stmt = connection.createStatement();
stmt.execute("INSERT INTO STUDENTS VALUES (" + student + ")");
} catch (Exception e) {
e.printStackTrace();
}
}
}
// Responsibility: Handle core student profile data
class Student {
private String studentId;
private Date studentDOB;
private String address;
public String getStudentId() {
return studentId;
}
public void setStudentId(String studentId) {
this.studentId = studentId;
}
}
student = new Student(...)
studentRepository = StudentRepository(...)
studentRepository.save(student)