단일 책임 원칙(Single Responsibility Principle, SRP)
정의
- 소프트웨어에서 하나의 컴포넌트는 단 하나의 책임을 가져야 한다.
- 소프트웨어 컴포넌트를 변경할 이유는 오직 하나여야 한다.
- 맥가이버 칼 같은 클래스를 만들지 말고, 단일 나이프를 만들어라.
적용
1. 각각의 컴포넌트는 응집력(Cohesion) 을 높이는 방향으로 설계해야 한다.
// Bad Example
public class Square {
int side = 5;
public int getArea() {
return side * side;
}
public int getPerimeter() {
return 4 * side;
}
public void draw() {
// draw the square image
}
public void rotate(int degree) {
// rotate the square to clockwise
// rerender the square image
}
}
- 위의 코드는
Square
클래스가 응집력이 높지 않다. getArea()
와getPerimeter()
는Square
의 정보를 반환하는 메서드이다.draw()
와rotate()
는Square
를 그리는 메서드이다.
// Good Example
public class Square {
int side = 5;
public int getArea() {
return side * side;
}
public int getPerimeter() {
return 4 * side;
}
}
public class SquareDrawer {
public void draw(Square square) {
// draw the square image
}
public void rotate(Square square, int degree) {
// rotate the square to clockwise
// rerender the square image
}
}
- 위의 코드는
Square
클래스와SquareDrawer
클래스로 분리하여 응집력을 높였다. Square
클래스는Square
의 정보를 반환하는 메서드만을 가지고 있다.SquareDrawer
클래스는Square
를 그리는 메서드만을 가지고 있다.
2. 컴포넌트 간의 결합력(Coupling) 을 낮춰야 한다.
// Bad Example
public class Student {
private String studentId;
private String studentName;
private Date studentBirth;
public void save() {
// Serialize object into a string representation
String objectStr = MyUtils.serialize(this);
Connection connection = null;
Statement statement = null;
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password");
statement = connection.createStatement();
statement.execute("INSERT INTO student VALUES (" + objectStr + ")");
} catch (Exception e) {
e.printStackTrace();
}
}
public String getStudentId() {
return studentId;
}
public void setStudentId(String studentId) {
this.studentId = studentId;
}
...
}
- save() 메서드는
Student
객체를 직렬화하여 DB에 저장하는 메서드이다. - 현재의 코드는
Student
클래스가 DB에 종속되어 결합력(Coupling)이 높다. - 만약 DB가 MySQL이 아닌 다른 DB로 변경된다면
Student
클래스의 코드를 수정해야 한다.
// Good Example
public class Student {
private String studentId;
private String studentName;
private Date studentBirth;
public void save() {
new StudentRepository().save(this);
}
public String getStudentId() {
return studentId;
}
public void setStudentId(String studentId) {
this.studentId = studentId;
}
...
}
public class StudentRepository {
public void save(Student student) {
// Serialize object into a string representation
String objectStr = MyUtils.serialize(student);
Connection connection = null;
Statement statement = null;
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password");
statement = connection.createStatement();
statement.execute("INSERT INTO student VALUES (" + objectStr + ")");
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 위의 코드는
Student
클래스와StudentRepository
클래스로 분리하여 결합력을 낮췄다. Student
클래스는 학생과 관련된 정보를 가지고 있다.StudentRepository
클래스는Student
객체를 DB에 저장하는 메서드만을 가지고 있다.
왜 소프트웨어 컴포넌트를 변경할 이유는 오직 하나여야 하는가?
- 소프트웨어 컴포넌트를 변경할 이유가 여러 개라면, 그 만큼 해당 컴포넌트를 변경하는 경우도 많아진다.
- 변경 은 버그를 발생시키는 가장 큰 원인이다.
- 때문에 소프트웨어 컴포넌트를 변경할 이유는 오직 하나로 두고, 변경을 최소화해야 한다.