리팩토링 16. 여러 함수를 클래스로 묶기
- etc/리팩토링
- 2023. 4. 30.
들어가기 전
이 글은 인프런 백기선님의 강의를 복습하며 작성한 글입니다.
리팩토링 16. 여러 함수를 클래스로 묶기 (Combine Functions into Class)
- 비슷한 매개변수 목록을 여러 함수에서 사용하고 있다면, 해당 메서드와 매개변수를 모아서 새로운 클래스를 만들 수 있다.
- 클래스 내부로 메서드를 옮기고, 데이터를 필드로 만들면 메서드에 전달해야 하는 매개변수 목록을 줄일 수 있다. 이를 통해 가독성 좋은 코드를 만들 수 있게 된다.
동일한 파라메터가 계속 같이 전달되는 행운이 있다면, 하나의 클래스로 메서드와 필드를 옮기기가 매우 편리하다. 그렇지만 대부분의 경우에는 코드들을 분석한 후에 전혀 관련 없어 보이는 매개변수들이 서로 관련이 있는 것을 찾아낸 후에 옮기게 되게 된다.
아무튼 어떤 방식으로든 관련있는 매개변수들을 찾아낸다면, 하나의 클래스로 옮긴다. 그리고 그 클래스에 매개변수를 사용하는 메서드들을 옮기면, 메서드가 사용하는 매개변수가 줄어든다.
Before
아래에서 프린터를 하는 Try ~ Resource 절을 살펴보자. 이 코드 아래는 몇 개의 메서드가 존재하는데, 이 메서드들은 프린터를 돕는 보조 메서드들이다.
프린터 + 보조 메서드들은 대부분 공통적인 매개변수를 참조하고 있다는 것을 알 수 있다. 아래 매개변수가 메서드들에서 반복적으로 매개변수로 쓰이고 있다.
- homework
- username
- totalNumberOfEvents
- totalNumberOfParticipants
또한, 여기에는 한 가지 매개변수가 더 필요한데 바로 Participants라는 매개변수다. 이것은 프린터하는 코드를 함수로 추출하려고 하면, 인텔리제이가 자동으로 Participants라는 매개변수가 필요한 것을 알려준다. 따라서 해당 코드들에는 아래 매개변수가 필요해진다.
- homework
- username
- totalNumberOfEvents
- totalNumberOfParticipants
- Participants
여기서 homework, username은 Participant에 속한 정보이며, Participant 정보는 Participants에 속해있다. 따라서 중복된 정보가 된다. 또한 totalNumberOfParticipants는 Participants의 사이즈로 구할 수 있다. 따라서 새로운 클래스의 필드로 사용할 녀석은 아래 2개로 추려진다.
- totalNumberOfEvents
- participants
이것을 고려해서 StudyPrinter 클래스를 만들어주고, 해당 클래스로 매개변수와 메서드를 옮겨주는 리팩토링을 진행해야한다.
// StudyDashboard.class
// 이 코드에서 출력을 담당한다.
// homework, username, totalNumberOfParticipants 등이 매개변수에 계속 전달되는 것을 볼 수 있다.
// homework, username은 Participant라는 객체에서 전달됨.
// 전달되는 이 매개변수들은 이 메서드에서만 쓰이므로, 메서드 + 매개변수로 따로 클래스를 뽑는 것이 코드 복잡도를 줄일 수 있음.
try (FileWriter fileWriter = new FileWriter("participants.md");
PrintWriter writer = new PrintWriter(fileWriter)) {
participants.sort(Comparator.comparing(Participant::username));
writer.print(header(participants.size()));
participants.forEach(p -> {
String markdownForHomework = getMarkdownForParticipant(p.username(), p.homework());
writer.print(markdownForHomework);
});
}
}
private String getMarkdownForParticipant(String username, Map<Integer, Boolean> homework) {
...
}
private String checkMark(Map<Integer, Boolean> homework) {
...
}
private double getRate(Map<Integer, Boolean> homework) {
...
}
private String header(int totalNumberOfParticipants) {
...
}
After
다음 작업을 진행한다.
- StudyPrinter 클래스를 생성한다.
- 클래스 필드는 Participants / totalNumberOfEvents를 받는다.
- Printer와 관련된 메서드를 옮긴다.
- 메서드를 옮기면서 불필요한 매개변수는 삭제해준다.
삭제하면 아래와 같이 완료된다. 관련된 데이터를 한 클래스로 모으고, 메서드도 한 클래스로 모으면 메서드가 사용하는 매개변수의 숫자가 줄어든다. 궁극적으로는 조금 더 메서드의 의미를 파악하기가 쉬워진다.
// 사용되는 매개변수를 하나의 클래스로 모으고, 각 메서드들에 필요한 매개변수를 줄인다.
public class StudyPrinter {
private final int totalNumberOfEvents;
private final List<Participant> participants;
public StudyPrinter(int totalNumberOfEvents, List<Participant> participants) {
this.totalNumberOfEvents = totalNumberOfEvents;
this.participants = participants;
}
public void print() throws IOException {
// 이 코드에서 출력을 담당한다.
// homework, username, totalNumberOfParticipants 등이 매개변수에 계속 전달되는 것을 볼 수 있다.
// homework, username은 Participant라는 객체에서 전달됨.
// 전달되는 이 매개변수들은 이 메서드에서만 쓰이므로, 메서드 + 매개변수로 따로 클래스를 뽑는 것이 코드 복잡도를 줄일 수 있음.
try (FileWriter fileWriter = new FileWriter("participants.md");
PrintWriter writer = new PrintWriter(fileWriter)) {
this.participants.sort(Comparator.comparing(Participant::username));
writer.print(header());
this.participants.forEach(p -> {
String markdownForHomework = getMarkdownForParticipant(p);
writer.print(markdownForHomework);
});
}
}
// 매개변수 2개 → 1개 감소
private String getMarkdownForParticipant(Participant participant) {
...
}
private String checkMark(Map<Integer, Boolean> homework) {
...
}
private double getRate(Map<Integer, Boolean> homework) {
...
}
// 매개변수 1개 → 0개 감소
private String header() {
...
}
}
'etc > 리팩토링' 카테고리의 다른 글
리팩토링 19. 질의 함수와 변경 함수 분리하기 (0) | 2023.05.01 |
---|---|
리팩토링 18. 변수 쪼개기 (0) | 2023.05.01 |
리팩토링 15. 플래그 인수 제거하기 (0) | 2023.04.30 |
리팩토링 14. 매개변수를 질의 함수로 바꾸기 (0) | 2023.04.30 |
냄새 4. 긴 매개변수 목록 (0) | 2023.04.30 |